When to use this endpoint

Use for the CRM2 layout’s header save. A full layout spreadsheet replacement has separate delete/create stages after this header operation; the route does not replace lines itself.

Authentication and permissions

Authenticate with signed API-key fields or a logged-in CRM session. Session requests use dispatcher CSRF protection when configured. API keys use their owner’s permissions.

Requires explicit View on Purchase Order or Purchase Orders, plus Edit on either page. Non-administrators with legacy Purchase Orders visibility disabled (crm_access bit 256) are denied.

Explicit order IDs must identify active, visible records in the authenticated tenant. TeamAccess follows linked Contracts, Customers, Jobs, Quotes and Departments; legacy department filtering follows Customer, Quote-customer and ordinary customer-Job links. Scoped users cannot read unlinked or Warehouse orders through this check.

Parameters

ParameterTypeRequiredDescription
idPositive scalar, native or JSON ID arrayYesExisting local header/line IDs. Every selected row is checked and all writes share one transaction; later failure rolls back the entire request.
supplierID / supplierInteger / native picker objectYes on creationAvailable, active, non-held supplier ID, or native supplier={id:42}. The supplier must be visible through its own page/record scope.
formIDInteger-compatibleNoDefault 0 on creation. A nonzero value must identify an active type-5 Purchase Order form.
referenceTextNoAt most 32 Unicode characters in mapped validation; omitted creation uses the database default. Unlike the legacy new route, no "no reference" fallback is added.
poNumInteger-compatibleNoDisplayed order number; distinct from row ID. Creation selects the next number under the owner lock when omitted, or rejects a supplied nonpositive/used number.
target / targetType / targetIDNative picker / integer-compatibleNotarget={type:4,id:87}, or separate targetType/targetID. Link types: 0 No Link, 1 Contract, 2 Customer, 3 Quote, 4 Job, 5 Department, 6 Location, 7 Warehouse. Types 0/7 use ID 0. This header link does not itself allocate stock.
statusInteger-compatibleNoReceived status: 0 Open (creation default), 1 Part Received, 2 Complete, 3 Over-received, 4 Cancelled. This is separate from approval, sent history and metaStatus.
deliveryDateISO dateNoDelivery date, YYYY-MM-DD. Mapped create/update validates SQL-date input; checked updateField additionally applies its strict real-date check.
deliveryAddress / attention / instructionsTextNoDelivery address, contact/attention and supplier instructions. Omission on update leaves existing values.
deliveryNet / deliveryTaxNumeric-compatibleNoCarriage net and explicit tax amounts. Supplying a delivery tax code does not calculate deliveryTax on the server.
deliveryTaxcodeID / deliveryTaxcodeText / native pickerNoStored carriage tax code, or picker {id:"T1"}. Do not substitute a percentage for the explicit deliveryTax amount.
orderNet / orderTaxNumeric-compatibleNoMapped order amounts. Recalculation replaces persisted header totals with stored line totals plus carriage; this is not a way to recalculate line amounts.
taxInc / rowData / roIDInteger-compatible / text / integer-compatibleNoTax-inclusion flag, legacy row payload, and linked Requisition Order ID. roID alone does not convert a requisition or move its allocations.
customValues / meta / invoiceReceivedJSON-compatible valuesNoStored custom values, workflow metadata and invoice marker. Creation defaults customValues to {}. Existing invoice endpoints are preferable for maintaining the invoice marker.
metaLink / metaStatus / metaOwing / metaPaymentList / metaOrderFullyReceivedDateMapped metadata valuesNoPaths within meta: link, status, owing, paymentList and orderFullyReceivedDate. The last path is a date; the others use mapped text/JSON conversion.
cDate / lastEdited / createdByID / createdBy / dateApproved / dateSentDate-time / integer / pickerLegacy controlsServer-owned metadata. Creation uses the current actor and server times; a createdBy picker may only select that actor. Changed creation/edit/approval/sent metadata is rejected; use the dedicated workflow actions.
widgetSpreadSheetNative objectNoNative layout control. A direct header call does not delete containers or save line rows. Layout_setValue owns atomic replacement of its purchaseOrderEntry spreadsheet.

Example request

$result = FM_api('PurchaseOrder_updateData', [
	'id'=>'1042',
	'reference'=>'Revised supplier reference',
]);

Example response

{
  "ok": 1
}

Errors and unusual responses

ResponseCondition
{"error":"not logged in"}No authenticated tenant context is available. Invalid signed credentials can be rejected by the dispatcher before the handler runs.
{"error": "permission denied"}Page/action rights or the legacy visibility setting deny access.
{"error": "purchase order does not exist"}An explicit parent/header ID is absent, deleted or outside the caller’s scope.
{"error": "missing or invalid parameter: id"}The shared updater has no usable ID.

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 same checked existing-header wrapper as dedicated setters: active scope, supplier/form/target validation, protected creation/sent/approval fields, real delivery dates, status 0–4/cancellation policy and finite nonnegative carriage.
  • Supplier, carriage and changed line data clear approval and require approval again. Exact no-op saves retain approval. Non-administrators with crm_access bit 8192 cannot make financial changes to approved orders.
  • Normalizes positive scalar/native/JSON ID selectors before writing. All selected headers commit together; each header receives its own recalculation, history and configured notification. Invalid later input rolls back the whole request.
  • Status maps to metaStatus 1 for Open, 3 for Complete, 7 for Cancelled and 0 for Part Received/Over-received. A transition to Complete can copy resources into a Job when pageGlobal.poCopyStockToJobOnComplete is exactly boolean true or integer 1; string "1" does not match.
  • A direct widgetSpreadSheet marker does not delete containers or replace rows. Layout_setValue owns full spreadsheet replacement: header, old-line deletion, new-line creation, availability, totals and approval commit together. Compatible allocations survive; an allocated/received identity or quantity change is rejected.
  • Completion-triggered Job copying in a layout runs after replacement lines exist, within the same transaction. Follow-up failure rolls back the complete save. No server line multiplication or tax-rate calculation is introduced.

Side effects

  • Writes use the tenant-owner lock and a transaction or caller savepoint. A rejected operation rolls back its local writes and checked history. Earlier separate API requests remain committed.
  • Edit events, notifications and form follow-ups run after the owning commit. A saved/no-replay warning identifies committed data with a failed follow-up; inspect the saved state rather than repeating creation or sending.
  • Writes mapped fields, checked header/field history and deferred update notifications. Recalculates delivery-inclusive totals for every selected header when relevant fields are present.

See it in Fieldmotion

CRM1

No direct CRM1 caller was found. CRM1 uses PurchaseOrder_new and the legacy PurchaseOrderEntry_new/save line editor instead.

CRM2

In CRM2 Purchase Order details, save the configurable layout and inspect Layout_setValue. Its purchaseOrder mapping dispatches this operation internally; the nested call need not appear as a separate browser request. The mobile route is separate. The mobile app goes through m/www’s separate PurchaseOrder_save/OrdersM workflow; its validation and ownership rules do not establish this office endpoint’s contract.

Related endpoints