When to use this endpoint

Use to review consumption recorded on completed customer jobs, not current Customer-type containers or all job statuses.

Authentication and permissions

Authenticate with a signed API-key request or a logged-in CRM session. Requires Stock view (pageStocks_view) and table export (pageMain_exportTableData) permissions through the shared export guard; denial returns permission denied.

Client administrators and the all permission satisfy the checks. Legacy users with no role use the helper’s allow fallback when no explicit permission list is loaded. This is not merely the CRM1 export-button bit check.

Queries use the authenticated tenant. No department/owner restriction or stock-tracking feature gate is added by these handlers. CRM2 features2.stockTracking changes the export choices offered, not server authorization. Session CSRF and API signing still apply.

Parameters

ParameterTypeRequiredDescription
yesStringYesMust equal really. This compatibility confirmation is checked before the handler login/permission checks; it does not authorize the export.
stockIDsFilterComma-separated stringNoA string such as 1042,1088, not a JSON or native array. Omitted or blank/whitespace means no selection restriction. Tokens must be positive decimal IDs; whitespace is trimmed and duplicates are removed. Malformed, zero/negative or non-string lists return invalid parameter: stockIDsFilter.
nameStringsNoNon-empty values add lower-case contains filters on the respective stock columns. SQL LIKE wildcards remain active.
codeStringsNoNon-empty values add lower-case contains filters on the respective stock columns. SQL LIKE wildcards remain active.
descriptionStringsNoNon-empty values add lower-case contains filters on the respective stock columns. SQL LIKE wildcards remain active.
archivedInteger-compatible stringNoOnly omission disables this filter. When present, integer-converts and matches is_archived, including blank/0 as unarchived; this is not a deleted-record filter.
category_idInteger-compatible stringNoNon-zero values search the stock category_ids JSON text for a quoted integer ID. It does not use the category membership CSV used to render category names.
stock_idInteger-compatible stringNoTruthy value adds an exact integer-converted stock ID filter.
location_tagsStringNoNon-empty value adds a contains expression on meta.location_tags using a safely encoded SQL literal. SQL LIKE wildcards remain active.
searchStringNoNon-empty value searches its own text against lower-case stock name OR code. Combines with other supplied filters using AND; SQL LIKE wildcards remain active.

Example request

$csv = FM_api('Stock_exportCustomers', ['yes'=>'really']);

Example response

Customer,"Filter cartridge"
"Example customer",3

Success is raw CSV, not JSON: retain the response body instead of JSON-decoding it. The response uses text/csv and the final download filename customers_stock.csv. Column positions and blank cells are meaningful.

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"}Either required export permission is denied.
{"error":"yes is not really"}yes is omitted or differs from really.
nothing to exportEligible job usage contains no stock IDs; this is raw text, not JSON.
Header-only CSV or interrupted outputMost database reads are not checked for failure. A blank export is not proof that the database contained no matching rows; malformed stored JSON can interrupt legacy reports.
{"error":"invalid parameter: stockIDsFilter"}A supplied selected-ID list is not a string of positive decimal IDs.
{"error":"invalid parameter: search"}A supplied stock/date filter is an array or other non-scalar value; the error names that parameter.
{"error":"could not create stock export"}The per-request CSV stream cannot be created, written or rewound.

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

Behaviour and edge cases

  • Reads non-deleted completed jobs with customer_type=0 whose stock_used text contains id; groups by customer_id ordered by that ID, not name.
  • Stock columns come from decoded stock_used IDs and current stock names. Stock and customer name lookups do not exclude deleted records. Shared filters narrow stock columns, not the jobs or customers.
  • Sums stock_used row amt per customer/item, with zero padding for columns without usage. Customers remain rows even if all selected stock columns have zero usage.
  • When no stock IDs were found across eligible jobs, emits plain text nothing to export instead of a CSV header. Historical malformed stock_used is not validated defensively.
  • Customer name fallback is UNKNOWN CUSTOMER ID:<id>. No date/status selector is consumed; CRM2 labels this report Customer (Completed Jobs), matching the completed-job predicate.
  • The download filename is customers_stock.csv.
  • No pagination or result limit is implemented. The entire result is exported; a header-only CSV is a valid empty result.
  • No stock quantity, audit, edit timestamp, notification or event is changed. Reads across multiple queries are not a transactionally frozen snapshot.
  • No direct caller for this exact endpoint was found in the current app JavaScript or m/www intermediary; these are office reporting/source-selection calls, not the app download protocol.
  • The shared guard rejects non-scalar supplied stock/date filters and validates any supplied stockIDsFilter even on reports that do not use it for selection.

Side effects

  • Builds CSV in a checked per-request php://temp stream, then streams it, closes it and exits. No shared tenant filename is written; concurrent requests generate independent files.

See it in Fieldmotion

CRM1

Open Stock, choose Customer in Export Stock and submit. Its filters select stock columns only; filter Network for Stock_exportCustomers.

CRM2

Open Stocks, choose Export, select the matching report and submit. Filter Network for Stock_exportCustomers (the posted form uses php/api.php with extra=/Stock_exportCustomers). CRM2 supplies yes=really and a comma-separated stockIDsFilter for the selected export scope.

Related endpoints