Jobs: forms and resources
Adjust job stock usage rows
/a/Job_setStockUsed
Add or subtract usage quantities, or replace rows for selected order sources, without moving inventory.
When to use this endpoint
Use for a usage-list adjustment with id/source/amt rows. Choose Job_setStock for the legacy Resources replacement flow that also adjusts stock levels.
For order replacement, include every replacement row for each requested order source; an empty request does not identify which source to clear.
Authentication and permissions
Authenticate with signed API-key fields or a logged-in CRM session. The handler performs no additional page or role-permission check.
Job and form IDs refer to the authenticated account. Cross-account writes occur only through the stored outsourcing relationship on the routes that explicitly mirror changes.
The dispatcher also applies account/user and IP restrictions. A CRM session carrying a CSRF token must supply it through X-FMCSRF or _csrf; missing/incorrect tokens can be rejected before this handler runs.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | Integer string | Yes | Required by presence, integer-converted without positivity validation. The referenced job must be active. |
stockUsed | JSON array string or array | Yes | Usage row list. Normal add/delete rows require id, source and amt; quantities are not validated as positive/numeric. Matching uses loose id/source comparison. Malformed nonarray input is wrapped and rejected in add/delete, but replaceOrder has weaker validation. |
option | String | Yes | Use add, delete or replaceOrder. There is no allowlist check: unknown values can still filter and rewrite old rows without applying the requested delta. |
stockUsed[].linkID | String inside stockUsed JSON | No | For replaceOrder, absent linkID is generated as r followed by current Unix seconds plus row position; it is not a uniqueness guarantee. |
Example request
$result = FM_api('Job_setStockUsed', [
'id'=>'1042',
'stockUsed'=>json_encode([['id'=>205,'source'=>'0_0','amt'=>2]]),
'option'=>'add',
]);
Example response
{"ok":1}
Errors and unusual responses
| Response | Condition |
|---|---|
{"error":"not logged in"} | No authenticated tenant context is available. Invalid signed credentials can be rejected by the dispatcher before the handler runs. |
{"error":"missing or invalid parameter: id"} | id is absent/null. |
{"error":"missing or invalid parameter: stockUsed"} | stockUsed is absent/null, or add/delete rows lack required members/array shape. |
{"error":"missing or invalid parameter: option"} | option is absent/null. |
{"error":"job does not exist"} | No job is read. |
{"error":"Unable to save job"} | The primary database save failed. Successful-save history and subsequent follow-ups are not run. |
{"ok":1,"warning":"Job saved; follow-up incomplete. Inspect saved state before retrying.","warnings":["Job_updateCosts: Unable to save job"],"saved":1} | The primary save succeeded, but a recalculation, asset rebuild, notification or outsourced mirror failed. warnings identifies the failed follow-up; any normal response fields remain present. A benign no-change result from a follow-up is not a failure. Read saved state before retrying. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- add increases the first matching old row’s amount, or appends a new row. A new unmatched row’s cost/price are overwritten from the stock catalog (missing stock defaults to zero); caller values are not preserved in that branch.
- delete subtracts amount from the first match and ignores unmatched requests. Both delta branches discard all old rows whose resulting amt is nonpositive. Replays apply the delta again.
- replaceOrder first identifies exact source tokens whose numeric prefix is 15 (requisition) or 16 (purchase), removes old rows with those exact sources, then appends every requested row. Other new source types append rather than replace. [] leaves existing order rows in place.
- replaceOrder does not enforce the add/delete row-shape checks or remove nonpositive rows. Missing cost/price fields are subsequently enriched using generic catalog prices without the job customer’s price band.
- Reads and updates only active jobs. No amount bounds, stock-active check, pagination, transaction or optimistic concurrency token is used. Unknown option strings are not a safe documented no-op.
Side effects
- Unchecked ID-only stock_used/timestamp/edit-marker UPDATE with meetings-cache invalidation. No direct history entry.
- Calls Job_updateCosts and Job_updatePrices; failures produce saved-state warnings. Deleted jobs are rejected before any usage changes.
- Does not deduct or return warehouse/container stock, record stock movements, queue threshold mail or mirror outsourced usage.
See it in Fieldmotion
CRM1
No direct canonical or legacy caller was found in CRM1’s source. Its Resources stock widget uses Job_setStock with the complete desired list instead.
CRM2
No direct canonical or legacy caller was found in CRM2’s source. Its layout-driven resource editing is a separate save contract.
Related endpoints
Job_getStockreads the job’s direct and field-specific stock usage before a resource edit.Job_setStockreplaces direct stock usage and adjusts legacy warehouse/container quantities.Job_updateCostsrefreshes cost/resource snapshots and invokes the price follow-up.