Skip to content

Purchasing API notes

This page is for the developer whose system posts purchase documents: which document types the module produces, how knock-off against an order or a goods receipt looks from outside, and what to poll after FINAL. It is the purchasing part only. There is one API for the whole platform, with no separate Purchasing API and no separate host for it, so the path grammar, access endpoints, authentication, paging, response envelopes and error codes are on the developer integration path.

The fast path for a purchase document your system posts:

  1. Read the source document’s open-queue rows; link only a line that still has open quantity (knock-off).
  2. Create the document by its server document type and write the document link to the source line. If you post invoices yourself, the GRN auto-convert must be off for that pair.
  3. Set it FINAL, then poll its Journal, Inventory and Tax posting statuses rather than reading balances straight back.

The document types this module produces

All of them are generic documents, reachable through the generic document endpoints by their server document type:

INTERNAL_PURCHASE_REQUISITION · INTERNAL_PURCHASE_QUOTATION · INTERNAL_BLANKET_PURCHASE_ORDER · INTERNAL_PURCHASE_ORDER · INTERNAL_PURCHASE_GOODS_RECEIVED_NOTE · INTERNAL_PURCHASE_GRN_STOCK_IN · INTERNAL_PURCHASE_GOODS_ISSUED_NOTE · INTERNAL_PURCHASE_INVOICE · INTERNAL_PURCHASE_INVOICE_NO_STOCK_IN · INTERNAL_PURCHASE_RETURN · INTERNAL_PURCHASE_DEBIT_NOTE · INTERNAL_PURCHASE_CREDIT_NOTE · INTERNAL_PURCHASE_REFUND_NOTE · INTERNAL_PURCHASE_CONSIGNMENT_ORDER · INTERNAL_PURCHASE_CONSIGNMENT_GRN · INTERNAL_PURCHASE_CONSIGNMENT_GIN · INTERNAL_PURCHASE_CONSIGNMENT_RETURN · INTERNAL_PURCHASE_CONSIGNMENT_INVOICE

Each applet’s reference page names the four API permissions its document type needs.

Consignor Purchase Billing is not a generic document. It writes its own billing tables through its own resource; there is no FINAL, no journal and no open queue behind it. The Supplier Delivery Order applet writes the tenant’s ordinary INTERNAL_OUTBOUND_DELIVERY_ORDER, not a purchase type.

How knock-off looks from outside

Knock-off is two tables, not a field. When a document goes FINAL, a processor reads the company’s knock-off configuration and writes one line open-queue row per line for each enabled downstream type, carrying the open quantity. A downstream document that knocks a line off writes a document link between the two and reduces the queue row’s open quantity.

Four consequences for an integration:

  • A document you create through the API is knocked off by writing the link, not by naming the source on the new document. Read the source’s open-queue rows first; a fully knocked-off line has no open quantity and must not be linked again.
  • Write the link through the links endpoint, after the document exists. The open quantity of a source line that has a queue row falls when the link arrives by POST /core2/tnt/dm/erp/generic-doc/links/backoffice-ep, the call the GRN applet makes once a receipt is saved. Where no queue row matches, the call still succeeds and nothing falls, so read the row back. A link carried inside the bl_fi_generic_doc_link array of the document you create is stored, but the queue row keeps its full open quantity, so the source line still shows as open to the next person who knocks off against it.
  • The queue exists only where the configuration says so. A finalised order in a company with no order → GRN row has no queue rows, and nothing downstream can find it.
  • Two backend converters copy a receipt into its invoice — plain GRN → Purchase Invoice, and GRN Stock In → No Stock In invoice — with the links already written. The company’s document-flow configuration decides whether they run and whether the result is DRAFT or FINAL. If you post invoices yourself, make sure the auto-convert is off for that pair, or you will get two.

Two things an integration must respect

The signums are the server’s, not yours. A document type’s quantity and amount signums are fixed in the backend, and the applet’s own copy is overwritten on every save. Sending a quantity signum of +1 on a plain GRN changes nothing; if you need the receipt to move stock you post a GRN Stock In, and you then bill with a No Stock In invoice. The receiving pair is a document-type choice, and an integration that mixes the pairs double-counts exactly as a user does.

Posting is asynchronous. Setting a purchase invoice to FINAL queues the work: one processor writes the journal, another the stock ledger lines, others the tax, cashbook and e-Invoice queues. An integration that finalises and immediately reads the supplier’s balance or the stock balance can legitimately see the old figure. Poll the document’s posting statuses — Journal, Inventory, Tax — rather than reading straight back. The _UPDATED webhook is sent when the FINAL is saved, before those processors run, so it is not the signal that anything posted. A journal that fails (a missing default GL code, most often) leaves the document FINAL with that status unposted; for a GRN Stock In the stock is already in.

What the API will not do for you

There is no matching endpoint, no tolerance check and no approval gate. An invoice posted with a price different from its knocked-off order finalises. If you need a variance rule, it lives in your system before you post.

Last updated on