Customers
Build and retrieve a customer stock export
/a/Customer_stockGetExportData
Support a two-phase customer or asset stock export by first selecting job IDs and then returning chosen export columns for those IDs.
When to use this endpoint
Use Customer_stockGetExportData through the CRM export widget. Phase one sends the sentinel _ids=1 to build an ID list from legacy customer filters; phase two sends a JSON ID array and JSON field list to retrieve export rows.
CRM2 already has job IDs from its newer table query and normally enters at phase two.
Authentication and permissions
The request must authenticate with signed API-key fields or a logged-in CRM session. The handler performs no additional page or role-permission check.
The lookup and any write use the authenticated tenant; an ID from another tenant cannot be addressed through this call.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
_ids | String or JSON array string | Yes | Exact string 1 selects phase one. Any other value must decode to a nonempty JSON array for phase two. |
customer_id | Integer string | Phase one | Positive target ID used in phase one. Despite the name, the target kind is controlled by customer_type. Existence is not checked. |
customer_type | Integer string | No | Phase-one job target type; defaults to 0 (customer). 1 represents an asset and 2 a location in the meeting target model. |
dateFrom | Date/time string | No | Phase-one inclusive lower bound on stored date_completed. When present, dateTo is also applied. |
dateTo | Date string | No | Phase-one inclusive upper date through 23:59:59; defaults to the server’s current date when dateFrom is present. |
daysFrom | Integer string | No | Phase-one relative lower day using the PHP server’s current date inside SQL. When present, daysTo is also used. |
daysTo | Integer string | No | Phase-one relative upper day; defaults to 0, with the next midnight exclusive. |
id | Integer string | No | Phase-one exact job ID. Zero and blank do not filter. |
username | String | No | Phase-one assigned-user name substring. No matching user forces an empty ID list. |
userList | Form array | No | Phase-one list of assigned user IDs. Used only when a nonempty array is supplied. |
fields | JSON array string | Phase two | One or more exact names: id, date_completed, stock_used, or user_id|user_type. Unknown fields reject the complete request. |
Example request
// Phase 1
$selection = FM_api('Customer_stockGetExportData', [
'_ids' => '1',
'customer_id' => '1088',
'customer_type' => '0',
'dateFrom' => '2026-09-01',
'dateTo' => '2026-09-30',
]);
// Phase 2
$rows = FM_api('Customer_stockGetExportData', [
'_ids' => json_encode(['7204', '7219']),
'fields' => json_encode(['id', 'date_completed', 'stock_used']),
]);
Example response
// Phase 1
{"ids":["7204","7219"]}
// Phase 2
[
{"id":"7204","f0":"7204","f1":"2026-09-24 11:00:00","f2":"[{\"name\":\"Filter\",\"code\":\"FLT-1\",\"categories\":\"[]\",\"quantity\":\"2\"}]"}
]
Phase-two aliases follow requested order: f0, f1, and so on, while a separate id is always present. user_id|user_type creates suffixed aliases such as f3_0 and f3_1.
stock_used is transformed into an encoded JSON array containing each current stock row’s name, code, categories, and the recorded quantity. Missing/deleted stock IDs are not explicitly normalized.
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: _ids"} | _ids is absent. |
{"error":"missing or invalid parameter: customer_id"} | Phase one receives no positive customer_id. |
{"error":"_ids should be a JSON array of numbers"} | A phase-two _ids value does not decode to a nonempty array. |
{"error":"fields should be an array of requested field names"} | fields is absent, empty, or not a JSON array. |
{"error":"unknown field name: name"} | A requested field is outside the four-name allowlist; name is replaced by that submitted value. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Phase one selects non-deleted jobs whose completion state is not NULL, including state
0, and whose mainstock_usedtext is non-NULL and containsid. It does not include stock found only in subforms. - Phase-one IDs are ordered ascending by job ID and are not paginated. The endpoint reads no
stockoractivefilter even though CRM1 includes both in its export-widget parameters. - Phase two integer-casts every submitted ID. It does not guarantee output order matches input order because the query has no
ORDER BY. - The
date_completedcompatibility branch checks anis_completevalue that is not selected by the allowlist, so the requested date is returned as stored rather than blanked for incomplete jobs. - The combined user field exposes the legacy underlying aliases; its attempted outsourced-user substitution depends on metadata that this endpoint does not select.
See it in Fieldmotion
CRM1
On a customer’s Stock Used tab, select Export. The export widget first calls Customer_stockGetExportData with _ids=1 and the page filters, then calls it again in chunks with JSON IDs and selected fields. CRM1 shows the button only when permission bit 256 is not set; the endpoint has no matching export-permission check.
CRM2
On CRM2 Customer Stock or Asset Stock, select export and filter Network requests for Customer_stockGetExportData. CRM2 collects IDs using CustomersStock_getDataTable and the export widget sends those IDs into this endpoint’s retrieval phase.
Related endpoints
Customer_stockGetDTis the legacy CRM1 visible job table whose filters seed phase one.Customer_stockTotalsGetDatasupplies CRM2 customer and asset total quantities.