Skip to content

Purchasing Cycle

Backend domains:

RBAC Paths: PURCHASE_REQUISITIONS, PURCHASE_ORDERS, SUPPLIER_INVOICES, EXPENSES, SUPPLIER_CREDITS.

Overview

flowchart LR
  PR[Purchase Requisition] -->|approve| PO[Purchase Order]
  SQ[Supplier Quote] -->|convert| PO
  PO -->|receive items| INV[Inventory ↑]
  PO -->|bill| SI[Supplier Invoice]
  SI -->|reversal| SC[Supplier Credit]
  SI -->|pay| PAY[Bank ↓ / AP ↓]
  EX[Expense] -->|one-off, no PO| PAY

A PYME workflow can be as light as "log the expense from a receipt" or as formal as the full PR → approval → PO → receive → 3-way match → pay flow.

Purchase Requisitions

A PR is the only domain today that runs through the approval framework (approval_configs/). It captures intent to buy: requested by an employee, routed to an approver, and converted to a PO once approved.

Key mutations: createPurchaseRequisition, updatePurchaseRequisition, submitPurchaseRequisitionForApproval, approvePurchaseRequisition, rejectPurchaseRequisition, deletePurchaseRequisition, convertPurchaseRequisitionToPo.

Key query: purchaseRequisition (single by id).

Supplier Quotes

suppliers/mutations/supplier_quote_mutations.py lets a buyer log multiple supplier offers for the same item set and pick a winner.

Key mutations: createSupplierQuote, updateSupplierQuote, deleteSupplierQuote, createExistingSupplierQuoteForSupplier (replicate to other suppliers).

Purchase Orders

A PO is the legal commitment to a supplier. It can be created from scratch, from a supplier quote, or even reverse-engineered from a supplier invoice ("I already received it, log the PO retroactively").

Key mutations: createPurchaseOrder, createPurchaseOrderFromSupplierQuote, createPurchaseOrderFromSupplierInvoice, updatePurchaseOrder, receivePurchaseOrderItems (partial receiving supported), deletePurchaseOrder.

Key queries: getPurchaseOrderById, getPurchaseOrderByNumber, getPurchaseOrderAmountsReport, getPurchaseOrdersBySupplierId.

Receiving a PO posts an inventory entry; the supplier invoice posts the GL leg.

Supplier Invoices

Same Invoice model as customer invoices but on the AP side. Mutations live under invoices/mutations/supplier_invoice_mutations.py and Strawberry types under invoices/strawberry/supplier_invoice_*.py.

Use cases: bill from a received PO, bill standalone, scan-and-parse a PDF/image via AI (planned), or import an XLSX batch via invoices/tabular/.

GL leg: Expense or Inventory ↔ AP plus ITBMS receivable for credits the tenant can take.

Expenses (one-off)

Expenses are non-PO disbursements: petty cash, fuel, utility bills paid on the spot. They support categorisation (expense_category_*) and post directly to the GL.

Key mutations: createExpenseCategory, updateExpenseCategory, deleteExpenseCategory, createExpense, updateExpense, deleteExpense.

Key queries: getExpenseById, getExpenseCategoryById, getExpenseCategoryByName, searchExpenseCategories.

For repeating expenses (rent, internet) see Recurring Expenses.

Supplier Credits

Mirrors customer credit notes for the AP side. Used when a supplier issues a credit (returned product, agreed discount, billing error).

Key mutations: createSupplierCredit, cancelSupplierCredit.

Key queries: getSupplierCreditById, getSupplierCreditsBySupplierInvoiceId.

Cross-cutting

  • Approval routing is currently wired only on Purchase Requisitions; see Approvals & Sequences.
  • Receipt vs. invoice: receiving items on a PO updates stock but does NOT post the GL — that's the supplier invoice's job. This separation lets accounts close cleanly when the bill arrives later.
  • 3-way match: PO ↔ Receiving ↔ Supplier Invoice is enforceable at the application layer today; full automated matching is a roadmap item.
  • RUC lookup: when entering a supplier, the RUC verification path (ruc-verification.md) can prefill legal name and DV.