Skip to content

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_CONSIGNMENT for consignments and consignmentInvoices. It can also dispatch and recall goods and decide returns through unpublished operations.
  • MANAGE_SETTLEMENTS for retailerStatements and retailerPayments. 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.

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.

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.

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 (or PAID when nothing is owed) and moves to PART_PAID or PAID as payments are set against it. WRITTEN_OFF also exists.
  • Online payments start as INITIATED and become APPROVED or FAILED. Payments declared by hand start as PENDING_APPROVAL and become APPROVED or REJECTED. An APPROVED payment can later be REVERSED.
  • Only APPROVED moves a retailer’s balance. Book only APPROVED payments, and watch for reversals. The receipt number is empty until a payment is approved.

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.

  • Consignment event fields use snake_case (dealer_id, unit_count). Order events use camelCase. Read each event’s fields as documented in Webhook events.
  • Amounts are whole paise in fields ending Paise. totalValuePaise on a challan is the GST-inclusive value at transfer price.
  • Return challans appear in consignments with kind: INBOUND_RETURN and name the original in originChallanNumber.
  • limit is capped at 100 for consignments and 200 for the other lists.