Suppliers & purchases
The other side of every CA's books. Track registered suppliers, record purchase bills with per-line ITC eligibility, feed the GSTR-2B reconciliation engine.
Suppliers
Same shape as your customers, with a few inward-only fields: GSTIN required (URD purchases are post-MVP; 2B-only purchases by definition come from registered suppliers), MSME status enum (none / micro / small / medium) for §43B reporting later, and a soft-delete so historical bills keep their FK integrity.
Routes: GET /suppliers, POST /suppliers,
GET /suppliers/{id}, etc. Trigram-indexed search
across name + GSTIN + phone. Same RLS isolation as customers —
tenant A cannot see tenant B's suppliers, ever.
Purchases
Header + lines, mirroring the invoice shape. The supplier's
invoice number is stored as supplier_invoice_no
(NOT our invoice_number — keeps the two ledgers cleanly
separated). Duplicate (supplier, invoice_no) is
rejected by a UNIQUE constraint with ErrDuplicateInvoice.
A single bill can mix eligible items (raw materials) with
blocked items (motor vehicle parts, food/beverages per §17(5)).
mb tracks eligibility per line, not per bill, because
GST law works per line. Each line's itc_eligibility
column takes one of three values:
§17(5) auto-suggestion from HSN.
Type an HSN code into a purchase line; if the prefix matches a commonly-blocked category, mb HTMX-swaps a hint into the row before you've finished typing the description. You can always override — the suggestion is a default, not a lockout.
| HSN prefix | CGST Act clause | Blocked category | Real-world example |
|---|---|---|---|
| 8702-8708 | §17(5)(a) | Motor vehicles + parts | Tyres for company van |
| 2106, 2202-2205 | §17(5)(b)(i) | Food, beverages, outdoor catering | Office snacks bill |
| 9603 | §17(5)(b)(ii) | Beauty / personal care | Salon services |
| 9961, 9962 | §17(5)(b)(iv) | Health insurance | Group mediclaim premium |
The suggestion engine is deliberately conservative — when in doubt, default to eligible. Real auditors set the rest manually. mb is a recon tool, not a compliance robot.
Every purchase feeds the 2B reconciliation.
When you upload GSTR-2B for a period, the matching engine pulls
your purchases for the same period and runs hash-keyed matching
on (supplier_gstin, invoice_number, RCM-flag). The
"missing-in-books" bucket in the recon view is exactly the set
of portal rows that didn't match a purchase you'd
already booked.
Click any of those → /purchases/new?from_2b={invoice_id}
pre-fills the form with the portal's amounts, including the
derived tax rate. Save → next page-load that row reclassifies as
Matched.