Consignment to ERP
Goal: keep your ERP in step with goods sent to retailers on consignment, the invoices raised when they sell, their monthly statements and the payments they make.
Permissions: these are two deliberately separate permissions. Ask only for the one your ERP needs.
MANAGE_CONSIGNMENTforconsignmentsandconsignmentInvoices. It can also dispatch and recall goods and decide returns through unpublished operations.MANAGE_SETTLEMENTSforretailerStatementsandretailerPayments. It can also adjust retailer ledgers and commission and mark settlements as paid through unpublished operations.
Both move goods or money. Treat the key as one of your most sensitive secrets.
Webhook events: CONSIGNMENT_DISPATCHED and CONSIGNMENT_SOLD. CONSIGNMENT_OVERDUE is defined but not sent at this build.
Queries: consignments, consignmentInvoices, retailerStatements, retailerPayments.
Recommended sync pattern
Section titled “Recommended sync pattern”Use the two events as prompts to read recent records for that retailer, and run a daily sweep. The events are not guaranteed: if raising one fails, the dispatch or sale still goes ahead and the event is lost. Several changes (receipts, returns, cancellations, statements, payments) have no event at all.
1. Turn dealer_id into a Dealer ID
Section titled “1. Turn dealer_id into a Dealer ID”Consignment events carry dealer_id as a plain number. The queries filter by a GraphQL Dealer ID, which is the Base64 encoding of Dealer: followed by that number:
const dealerGid = Buffer.from(`Dealer:${event.data.dealer_id}`).toString('base64');dealer_gid = base64.b64encode(f"Dealer:{event['data']['dealer_id']}".encode()).decode()The dealerId field on a consignment or payment is in the same form, so you can compare them directly.
2. On CONSIGNMENT_DISPATCHED, read that retailer’s recent challans
Section titled “2. On CONSIGNMENT_DISPATCHED, read that retailer’s recent challans”query RetailerChallans($dealerId: ID!) { consignments(dealerId: $dealerId, limit: 20) { number kind status unitCount unitsOutstanding totalValuePaise totalTaxablePaise placeOfSupply dispatchedAt acknowledgedAt ewayBillNumber dealerId dealerName lines { sku hsnCode gstRate quantity quantityAccepted quantityRejected unitTransferPricePaise lineValuePaise } }}{ "dealerId": "RGVhbGVyOjA=" }Upsert by challan number. The event’s data.number tells you which challan is new.
3. On CONSIGNMENT_SOLD, read that retailer’s recent invoices
Section titled “3. On CONSIGNMENT_SOLD, read that retailer’s recent invoices”Each reported sale raises a consignment tax invoice. The event carries the SKU and transfer price but not the invoice number, so read the retailer’s latest invoices and upsert by number:
query RetailerInvoices($dealerId: ID!) { consignmentInvoices(dealerId: $dealerId, limit: 20) { id number status origin issuedAt unitCount totalValuePaise totalTaxablePaise placeOfSupply cancelReason dealerName }}status is ISSUED or CANCELLED. A cancelled invoice keeps its row and gains a cancelReason, so re-read recent invoices rather than reading each one once.
Not every invoice follows a sale event. origin is SALE_REPORT for a reported sale and FORCE_INVOICE for goods invoiced at the six-month deadline; the second kind sends no event, and your daily sweep picks it up.
4. Sweep statements and payments
Section titled “4. Sweep statements and payments”Neither has an event. Page each list newest first with offset, and upsert by number (statements) or id (payments):
query StatementsPage($offset: Int!) { retailerStatements(limit: 100, offset: $offset) { id number status periodStart periodEnd issuedAt dueDate openingBalancePaise salesPaise creditsPaise adjustmentsPaise receiptsPaise closingBalancePaise outstandingPaise overdue dealerName }}query PaymentsPage($offset: Int!) { retailerPayments(limit: 100, offset: $offset) { id number status amountPaise method gateway receivedAt approvedAt reference reversalReason dealerId dealerName allocations { statementNumber amountPaise } }}- Statements are issued monthly, early on the 1st by default (staff can change the schedule). A statement starts as
ISSUED(orPAIDwhen nothing is owed) and moves toPART_PAIDorPAIDas payments are set against it.WRITTEN_OFFalso exists. - Online payments start as
INITIATEDand becomeAPPROVEDorFAILED. Payments declared by hand start asPENDING_APPROVALand becomeAPPROVEDorREJECTED. AnAPPROVEDpayment can later beREVERSED. - Only
APPROVEDmoves a retailer’s balance. Book onlyAPPROVEDpayments, and watch for reversals. The receiptnumberis empty until a payment is approved.
5. Daily reconciliation
Section titled “5. Daily reconciliation”Once a day, page all four lists back to the start of your window (a page shorter than limit is the last one) and re-read records whose status can still change. This catches lost events, receipts, returns and cancellations.
Things to know
Section titled “Things to know”- Consignment event fields use
snake_case(dealer_id,unit_count). Order events usecamelCase. Read each event’s fields as documented in Webhook events. - Amounts are whole paise in fields ending
Paise.totalValuePaiseon a challan is the GST-inclusive value at transfer price. - Return challans appear in
consignmentswithkind: INBOUND_RETURNand name the original inoriginChallanNumber. limitis capped at 100 forconsignmentsand 200 for the other lists.