Goods Received
Create a receipt with optional stock intake
/a/GoodReceived_createData
Create a Goods Received header and optionally run the CRM2 receipt-widget stock intake.
When to use this endpoint
Use the mapped CRM2 creation path, with widgetGoodReceivedEntry only when stock really arrived. Omitting the widget creates just a header; updating a header or adding a mapped line later is not an equivalent stock intake.
Authentication and permissions
Authenticate with signed API-key fields or a logged-in CRM session. Session CSRF, account-active and IP restrictions still apply. These handlers add no receipt-specific page, finance, department or record-owner permission check.
Records are scoped to the authenticated tenant. CRM1 displays Goods Received with employer feature bit 262144; CRM2 checks features2.goodsReceivedNotes. Those screen gates are not enforced by these handlers. Mobile receipt routes run separately through the mobile intermediary and have different permission and ownership checks.
Mapped handlers require both a logged-in user and a tenant context before loading the tenant field map.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
supplierID, poID | Integer | No | Supplier and purchase-order IDs, converted with intval. No existence, approval, supplier/PO consistency or ownership validation. |
cDate, lastEdited | Date/time string | No | cDate defaults to server now; when provided and nonblank it must be YYYY-MM-DD or YYYY-MM-DD HH:MM:SS (date-only normalizes to midnight). No tenant-timezone conversion. lastEdited is a mapped creation timestamp; do not use it to override update timestamps. |
createdBy | Integer | No | Stored created_by; GoodReceived_createData forces the authenticated user. Other mapped writes do not enforce that identity. |
notes | Text or native structured value | No | Whole notes replacement; native arrays/objects are JSON-encoded. UI uses structured notesboxReverse notes. |
target | Native object | No | Composite {id,type} expands to targetID / targetType. UI choices: 0 no link, 1 contract, 2 customer, 3 quote, 4 job. Numeric aliases themselves are not enum-validated. |
targetID, targetType, jobID | Integer | No | Direct stored link aliases. jobID is writable despite UI readonly metadata; targetType/targetID do not synchronize jobID. |
suppInvoice, suppDNN | Text | No | Supplier invoice and delivery-note references; no uniqueness or supplier validation. |
purchaseOrders | Text | No | Stored purchase_orders value. UI readonly metadata is not a server write restriction; this is not the poID alias. |
customValues | JSON text or native object | No | Whole custom_values replacement; creation defaults to {}. No per-key merge. Invalid stored JSON can be rejected by the database. |
noRecord, noRecordCustom | Native arrays | No | Legacy history-hidden controls, not authorization or no-write switches. noRecordCustom controls custom-key history; do not rely on noRecord to suppress creation history because the helper compares a reused loop key. |
widgetGoodReceivedEntry | Native object | No | Native object containing poID, status, isDispatch and a native rows list. Each row has typeID (1 catalog / 2 free text), stockID, description, outstanding (positive received quantity, up to four decimal places), poeID, stockStorage (default 1), cost (default 0), and reference:{type,id}. Catalog stock and storage must be active. Serial type 1 requires one unique nonblank serial per received unit, separated by newlines; serial count must equal the quantity. Per-row dispatch is ignored. |
Example request
FM_api('GoodReceived_createData', [
'supplierID'=>'84',
'notes'=>'Delivery received',
'widgetGoodReceivedEntry'=>[
'poID'=>'0', 'status'=>'2', 'isDispatch'=>'0',
'rows'=>[[
'typeID'=>'1', 'stockID'=>'318', 'outstanding'=>'2',
'stockStorage'=>'1', 'cost'=>'12.5',
'reference'=>['type'=>'2', 'id'=>'BATCH-2026-10'],
]],
],
]);
Example response
{"ok":1,"id":"531"}
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":"Unable to create record"} | The initial mapped header insert failed; dependent widget work is not started. |
{"error":"serial reference count must match received quantity; blank or duplicate serials are not allowed"} | Serial input fails preflight; no header is created. |
{"error":"goods receipt write failed","id":"531","warning":"Receipt header saved; intake may be partial. Inspect receipt and stock history before retrying."} | A later checked write fails after the header was created; earlier effects are not rolled 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
- Mapped integer fields use intval; UI hidden/readonly metadata is not a server authorization rule. Unmapped keys are ignored. The literal string
nullis treated as SQL NULL even for ordinary text, while omitted/null keys are generally skipped by isset. - Creation supplies createdBy from the authenticated user. No supplier or PO is required by the header map; an empty request can create a dated header. Omitted widget rows become an empty list, but malformed/non-native supplied rows are rejected. An empty widget can still change PO status/date.
- For a linked PO, the handler copies its target into the header and maps it to a stock allocation source. It loads matching PO/requisition order_containers and consumes reservations in query order, then leaves excess quantity unallocated. It does not validate ordered-versus-received limits.
- Catalog rows create StockInventory records and Stock_transaction intake history. Batch quantities use outstanding; serial intake creates exactly one inventory unit per validated serial. Serial counts must match the recorded receipt quantity before any header is saved.
- It allocates received stock to reservation destinations and rewrites matching job stock_used sources. With isDispatch=1 and a PO target mapped to Job, it calls Stock_dispatch; completed jobs may additionally receive outgoing movements to their customer/asset.
- Job substatus updates run when autoupdate-job-substatus is blank or 1. features2.stockTracking changes allocation/dispatch priorities and stock helper negative-stock rules; it is not an endpoint access gate. Job_syncStockUsedForm is called with option=1.
- A positive widget poID triggers PurchaseOrder_updateData with the supplied status and server-today deliveryDate. Status 2/3 stamps metaOrderFullyReceivedDate to server today, not a backdated cDate. pageGlobal.poCopyStockToJobOnComplete can trigger additional job-copy effects inside that PO helper.
- Each receipt line stores meta.storage and meta.reference; serial references are arrays. Reservation allocation consumes the actual created inventory units across destinations. Partial job stock-used splits retain the correct remainder and only match the current catalog/free-text item.
- Requires tenant upgrade 798 for fractional receipt amounts. More than four decimal places, negative/zero widget quantities, blank/duplicate serials, mismatched serial counts and missing catalog/storage records are rejected during preflight.
Side effects
- Header and line writes/cache invalidations; catalog and inventory stock intake, movement history, reservation updates/removal, optional dispatch, job stock_used/substatus/form sync, PO status/dates and PO helper notifications/history.
- sendNotices runs after header persistence for structured notesboxReverse mentions; failures are not propagated. Existing notes are not a substitute for checking stock movement success.
See it in Fieldmotion
CRM1
No direct CRM1 caller was found. CRM1 uses the legacy GoodsReceived_newNote, GoodsReceivedEntries_get and GoodsReceivedNote_* workflow instead.
CRM2
In CRM2 open Goods Received and a receipt layout, then filter browser requests for Layout_getValue / Layout_setValue. The goodReceived table mapping dispatches header reads to GoodReceived_getLayoutDetails and creates/updates to GoodReceived_createData / GoodReceived_updateData. Use Create and the Goods Received entry widget; the UI rejects empty widget rows, unlike the endpoint.
Related endpoints
GoodsReceived_newNoteโ legacy positional intake with different stock effects.GoodsReceivedEntry_createDataโ metadata-only line insert.GoodReceived_getLayoutDetailsโ read the created header/widget.