When to use this endpoint

Use after creating a blank CRM1 row with Entry_new. Submit the complete row; omitted legacy quantity/cost/total fields are not a patch operation.

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 line IDs are checked against their active, visible parent orders. A supplied po_id must match the line’s owner. These are tenant-local record IDs, not displayed order numbers.

Parameters

ParameterTypeRequiredDescription
idPositive integer or digit stringYesExisting local line ID; its parent must be active and visible.
po_idPositive integer or digit stringYesThe line’s actual parent header. A mismatched parent fails before writing.
stock_type / stock_idInteger-compatibleWorkflow inputType 1 catalogue or 2 miscellaneous; stock_id is the catalogue ID, normally 0 for free text. Values are integer-converted without an allowlist/reference lookup here.
stock_textTextNoDefault empty, replacing existing text. Maximum 255 Unicode characters; longer/invalid text is rejected.
qty / costNumeric-compatibleWorkflow inputFull replacement quantity/unit cost. Must be finite/nonnegative; fractional quantity is retained.
totalnet / totaltaxNumeric-compatibleWorkflow inputFull replacement line net/tax. These are trusted supplied amounts, not calculated from qty/cost or the tax code.
nominal_id / nominal_code / nominalLocal ID / code / legacy valueNoCanonical nominal account selection. Explicit ID and code must agree; legacy nominal is resolved code-first, then as a local ID. Blank/omission clears the nominal selection.
taxtypeTextNoStored tax code; omission clears it. No server tax calculation occurs.

Example request

$result = FM_api('PurchaseOrderEntry_save', [
	'id'=>'2081','po_id'=>'1042',
	'stock_type'=>'2','stock_id'=>'0','stock_text'=>'Packing materials',
	'qty'=>'1.5','cost'=>'10',
	'totalnet'=>'15','totaltax'=>'3',
	'nominal'=>'','taxtype'=>'',
]);

Example response

{
  "ok": 1,
  "id": 2081,
  "total_net": 15,
  "total_tax": 3
}

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": "purchase order entry belongs to another order"}The supplied po_id does not own this line.
{"error": "Purchase order line text must be 255 characters or fewer."}stock_text exceeds 255 Unicode characters.
{"error": "Editing an approved Purchase Order is disabled for your account"}The approved-edit role restriction blocks the write.
{"error":"Cannot change stock quantities or identity on an allocated or received Purchase Order"}The change would invalidate existing allocations or receipts; the transaction rolls back.

Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.

Behaviour and edge cases

  • Full replacement of the consumed legacy row fields, not a patch. Quantity, cost and supplied line net/tax must be finite/nonnegative; stock type must be 1 or 2 and text fits 255 Unicode characters.
  • Recalculates the stored parent from all saved line totals plus carriage. Response total_net/total_tax are aggregate line-only amounts for the whole order, excluding carriage; they are not the edited line amounts.
  • Nominal selectors resolve ID/code conflicts and store the canonical accounting code. No quantity × cost or tax-code calculation is performed on the supplied totals.
  • 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.
  • Initial availability is reconciled for identity/quantity changes. Once stock is allocated or received, incompatible identity/quantity changes fail and roll back; destination allocations are retained. A legacy blank row’s first populated save records create-line history.

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.
  • Checked row update, parent totals/timestamp/cache, availability, line history and approval reset commit together. The edit event follows commit.

See it in Fieldmotion

CRM1

In CRM1 Purchase Order details, edit quantity, cost, stock or accounting cells and inspect PurchaseOrderEntry_save. The browser supplies computed totalnet/totaltax.

CRM2

CRM2 uses mapped spreadsheet replacement rather than this legacy save payload. 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