Skip to content

Historical Stock Balance

historical-stock-balance is the query behind the Stock Report applet’s balance-as-at-a-date screen — one row per item, with the balance summed across every location you asked for, computed as of a date_to you supply (“Date As” on the applet).

Path: POST /core2/tnt/dm/erp/reports/stock/historical-stock-balance/etl-ep Request body: StockReportInputDto as JSON Response envelope: StreamingPagingResponse — see the family page

See Applet Reports API for authentication and the shared envelope shapes.


Request fields

FieldTypeDefaultApplied by this query?
location_guidsSet<UUID>emptyYes, twice over: it picks the stores whose balances are added up, and the companies whose costs are used (the companies that own those stores). The balances of all the stores you send are summed into one row per item — to see each store on its own, call once per location.
date_fromZonedDateTimenowNo — send it if you like, it’s ignored. The applet itself hardcodes 2022-01-01T00:00:00.000Z and never varies it; there’s no lower bound in the query the value could affect anyway.
date_toZonedDateTimenowYes — this is “Date As”. The query includes everything up to and including the full calendar day of date_to (internally: < date_to + 1 day), so a timestamp with a time-of-day component doesn’t trim same-day transactions.
keywordOptional<String>emptyCase-insensitive contains-match on the item’s code, name or description as the item master holds them today. Narrower than the sales report’s keyword: no scan code or alternate codes, and spaces are not wildcards.
item_from, item_toOptional<String> eachemptyAn inclusive range on item code, compared as text — A10 sorts before A9. Applied only when both are present; send one alone and it is silently dropped, returning every item.
item_type, item_status, item_category1_guids … item_category20_guidsSet<String> eachemptyMatched against the item master today, not as at date_to: an item deactivated after the date still held stock on it, but drops out of item_status: ["ACTIVE"].
pre_order_item, require_delivery, require_production, consignment, free_shipping, basic_warrantyOptional<Boolean> eachempty (no filter)Each tests one switch on the item master today (pre-order, delivery, production, consignment, free shipping, basic warranty). Omit a field to ignore that switch — sending false is a filter, not “don’t care”.
show_serialOptional<Boolean>emptyYes — adds a serials array per row; slower, only request it if you’re using it
company_guidsSet<UUID>emptyNo — read nowhere in this call path. The companies searched are derived from location_guids (LocationUow.grabCompanyGuidsBasedOnLocationGuids), not from this field.
txn_typeSet<String>emptyNo — hardcoded. This method always filters to PNS, ETL_SYNC_STOCK_BALANCE_CALIBRATION and ETL_SYNC_STOCK_DELTA_REPLICATION for the balance figure and PNS/RESET_MA for the cost figure. Whatever you send here is never read.
date_columnString"txn_date"Ignored. The balance is always cut by stock transaction date (txn_date); this route cannot be switched to another date column the way some sibling stock reports can.
hide_voided_documents, include_sn_adjustment, inv_item_guids, option_balancevariousemptyNo — not referenced by this query. Present on the DTO for other StockReportController endpoints.
date_from is not optional to get right — it’s fixed, and you should fix it the same way. Always send "2022-01-01T00:00:00.000Z", matching what the applet itself sends on every search, so a future reader of your integration’s code isn’t misled into thinking it does something. It is accepted and parsed, just never placed in a WHERE clause anywhere in this query.

Server-side transforms already applied

Two things the applet does client-side are done for you before the response leaves the server:

  1. Zero-balance rows are dropped. The applet’s own default (showZeroBalance = false) is applied unconditionally — there is no request field to turn this off and get zero-balance rows back. If your integration specifically needs them, you cannot get them from this endpoint; query bl_inv_current_company_stock_balance directly instead.
  2. hist_ma_amount and hist_ma_price are added, computed as cost_ma_amount and cost_ma_amount / bal_qty respectively.

Cost basis: this is the one thing the endpoint does NOT replicate — you still have to

The applet’s “Calculate Base On” dropdown is never sent to the backend — same as on Sales Report By Document, it’s a purely client-side relabelling. But unlike that page, what it relabels here isn’t just a display fallback — it changes which raw field the applet’s hist_ma_amount/hist_ma_price columns actually show:

// what the applet does, client-side, for every row:
hist_ma_amount = row[selectedCostBasis + '_amount']   // e.g. row['cost_fifo_amount'] if FIFO is selected
hist_ma_price  = hist_ma_amount / row.bal_qty

where selectedCostBasis is cost_ma by default and one of cost_ma, cost_fifo, cost_wa, cost_lifo, cost_replacement, cost_manual depending on what the user picked.

This endpoint’s own hist_ma_amount/hist_ma_price are always MA-based (see above) — they match the applet’s output only when the applet’s “Calculate Base On” is left on its default, MA. If your integration needs to reproduce the applet under a different cost basis, ignore this endpoint’s hist_ma_* fields and compute your own from the raw cost_fifo_amount / cost_wa_amount / cost_lifo_amount fields, the same way the applet does. cost_replacement_amount and cost_manual_amount are not in this endpoint’s response at all — the underlying query never computes them — so “Replacement” and “Manual” cost bases cannot be reproduced from this endpoint under any circumstance; if a customer needs those, they need the raw bl_inv_current_company_stock_balance figures instead.

Response fields

FieldNotes
guid, guid_fi_mst_itemItem identity. guid_fi_mst_item is the item master’s GUID — join on it rather than on code, which can be changed.
code, name, uomRead from the item master at the time of the call, not as at date_to — a balance for last year comes back under this year’s code and name. Rows are sorted by code.
category1_code … category10_code, category1_name … category10_nameItem category tree — 10 levels on this endpoint (the sales report joins 20).
bal_qtyThe last stock balance on or before date_to in each location you asked for, summed across those locations. A negative balance in one store offsets stock in another. Never zero — zero rows are dropped (see below), but negative totals do come through.
cost_ma_priceCurrent, live moving-average unit cost. Despite the report being “historical,” this specific field is not point-in-time — it reads today’s MA price, not the MA price as of date_to.
cost_last_purchase_companyThe item’s company-level last purchase cost, taken from the same balance row as cost_ma_price. Added to the row on 30 September 2026 (backend 5e79d87bdc) for the applet’s new Last Cost column; absent on earlier builds.
cost_ma_amount, cost_fifo_amount, cost_wa_amount, cost_lifo_amountCost-basis amounts, each computed as bal_qty × the corresponding per-unit cost as of date_to in the query (unlike cost_ma_price, these ARE point-in-time). cost_replacement_amount and cost_manual_amount do not exist on this endpoint.
hist_ma_amount, hist_ma_priceAdded by this endpoint — see Server-side transforms and the cost-basis warning above for exactly what these are and aren’t.
serialsPresent only when show_serial: true. Array of {serial_no, quantity, location_guid}.

Known data issue: some items have two conflicting balance rows

Some items can have two rows in bl_inv_current_company_stock_balance for the same item and company, under two different inv_item_guid values, with no deterministic ordering between them. This is a pre-existing condition in the source data — not something introduced by this endpoint, and not something this endpoint (or the applet itself) corrects, because the query this endpoint calls has no ROW_NUMBER()/dedup on that particular join, unlike every bl_inv_txn_line-based CTE earlier in the same method, which does. See this page’s sources: map for exactly where.

Practical effect: for an affected item, cost_ma_price (and anything derived from it) can come back as either of the two conflicting values on different calls, non-deterministically. This showed up during verification as the applet itself disagreeing with its own export across two pulls of the same item — so it is not specific to etl-ep; a frontend built on this endpoint has the same exposure the applet already has. There is nothing to work around on the client side; this needs a backend fix (add the missing ROW_NUMBER() partition) to resolve for good.


Filter dropdowns

POST /core2/tnt/dm/erp/drop-down/company/etl-ep       (narrows the Location dropdown)
POST /core2/tnt/dm/erp/drop-down/location/etl-ep
POST /core2/tnt/dm/erp/drop-down/label/etl-ep
POST /core2/tnt/dm/erp/drop-down/label-list/etl-ep

Body and response shape: see Applet Reports API → dropdowns.


Example

curl -X POST "https://api-etl.akaun.com/core2/tnt/dm/erp/reports/stock/historical-stock-balance/etl-ep" \
  -H "AccessId: YOUR_ACCESS_ID" \
  -H "AccessKey: YOUR_ACCESS_KEY" \
  -H "tenantCode: YOUR_TENANT_CODE" \
  -H "Content-Type: application/json" \
  -d '{
    "location_guids": ["6b8d1f42-7e35-4c09-ad81-1d4e8b2c5a70"],
    "date_from": "2022-01-01T00:00:00.000Z",
    "date_to": "2026-09-01T00:00:00.000Z"
  }'

Related

Last updated on