Jobs: forms and resources
Recalculate job costs and prices
/a/Job_updateCosts
Refresh labour, mileage and resource cost totals, backfill missing resource snapshots, then recalculate sell totals.
When to use this endpoint
Use after a resource or duration change when dependent financial fields need refreshing. Many job setters already invoke this internally.
Read the job afterward to inspect persisted totals; the response is an acknowledgement rather than a cost breakdown.
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 | Positive physical job ID in the authenticated account. |
Example request
$result = FM_api('Job_updateCosts', ['id'=>'1042']);
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"} | Nonpositive job ID. |
{"error":"job does not exist: 1042"} | No active job is read; the requested ID is interpolated. |
{"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_updatePrices: 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
- Uses the assigned users row’s cost_per_hour. When the job lacks vehicle_id, adopts that user’s default vehicle, then multiplies mileage by the vehicle’s cost_per_distance; numeric IDs and UUIDs are supported in the vehicle lookup. User/vehicle lookups do not enforce active state.
- Labour begins with hours×60 and tenant-local meeting time; statusInProgress.timestamp (milliseconds) overrides the start using PHP’s current timezone. When travel-is-labour-cost is enabled and statusOnRoute precedes it, travel extends labour minutes.
- Splits a single minute-of-day interval using work-day-start/work-day-end and overtime-multiplier (falsey multiplier becomes 1). This is not a per-calendar-day/weekend/holiday calculation for multi-day jobs.
- Calculates meta.minutes_worked in memory for labour pricing but does not include that metadata in its UPDATE. Existing saved breakdowns are therefore retained, not refreshed by this call.
- Direct stock cost is sum(cost×amt); missing cost/price fields use average cost and the resolved customer price band, preserving explicit snapshots. Table stock cells contribute only their first stock entry to this helper’s cost sum.
- Form meta.do_not_add_stock_to_job_costs=1 excludes matching formTable-tagged direct rows and saved table cells from cost totals without removing stored usage. SOR snapshots are enriched only when missing and summed to three decimals.
- Misc rows use cost_total when present, otherwise cost×(qty or amt). A nonempty misc list recalculates; an empty/invalid list retains the prior misc_cost rather than zeroing it.
- Only changed totals, vehicle or enriched resources trigger the cost UPDATE/history. Job_updatePrices is always invoked, even when costs were already current. No inventory movement, paging or filter parameters.
Side effects
- Conditionally writes vehicle_id, hours_cost, mileage_cost, stock_cost, sor_cost, misc_cost and enriched stock/table/rate JSON, plus edit timestamps; invalidates meetings cache and logs job history.
- Then refreshes price_net/price and, when configured, meta.job_value through Job_updatePrices. The cost UPDATE is checked; price follow-up failures produce a saved-state warning rather than implying that saved costs were rolled back. The phases are not transactional.
- Does not create/edit invoices, move stock, send direct mail or mirror outsourced costs.
See it in Fieldmotion
CRM1
No direct canonical or legacy caller was found in CRM1’s source. Other job setters call this internally; those nested calls are not separate browser Network requests.
CRM2
No direct canonical or legacy caller was found in CRM2’s source. Its layout-driven resource editing is a separate save contract. The Costs screen directly finishes with Job_updatePrices; cost setters can invoke this helper internally.
Related endpoints
Job_updatePricesrefreshes sell totals after price-bearing resource changes.Job_setStockreplaces direct stock usage and adjusts legacy warehouse/container quantities.Job_setScheduleOfRatesreplaces rate usage and persists its cost snapshots.