Skip to content

Supplier Credit Application — supplier credits now actually do something

Date: 2026-06-27 Status: shipped Audit findings: #36–#39 — supplier credits could be created but the whole application/cancellation flow was a shell.

Problem / motivation

A supplier credit note (the supplier crediting us, reducing what we owe) could be created, but did nothing: no ledger posting, supplier_credit_status stuck at UNAPPLIED, due_amount hardcoded to 0, the supplier_invoice_detail_id line link never read, and cancel was a no-op. The sales-credit flow, by contrast, is fully wired — this brings the supplier side to parity.

What shipped

Creating a supplier credit linked to a supplier invoice now applies it:

  • GL posting via the new SupplierCreditLedgerService.record_supplier_credit: DR Accounts Payable (we owe less) and CR the line GL account (expense/inventory — unwinding the original purchase). This mirrors the supplier-invoice posting in reverse and balances by construction.
  • Invoice settlement: the linked supplier invoice's canceled_amount rises and its due_amount falls; each credited line's canceled_quantity / canceled_amount is rolled forward via the previously-dead supplier_invoice_detail_id link (audit #39).
  • Status + balance: supplier_credit_statusAPPLIED, and the credit's own due_amount is computed (full when unapplied, 0 once applied) instead of a hardcoded 0 (audit #38).

cancel_supplier_credit now genuinely reverses everything: it reverses the GL entry, restores the invoice's outstanding balance and line cancellations, and sets the status to CANCELED (audit #36/#37).

Bug fixed along the way

SupplierCreditDetailInput.to_orm_model passed note=self.note raw, leaking strawberry.UNSET into the DB insert (every other detail input uses optional_field). Fixed — supplier credits with no line note can now be created.

Files

Data model / GraphQL surface

No schema change — all fields (supplier_credit_status, due_amount, supplier_invoice_detail_id) already existed; this wires the behaviour. The createSupplierCredit / cancelSupplierCredit mutations keep their signatures.

Frontend impact

  • Creating a supplier credit against a supplier invoice now reduces that invoice's outstanding balance and posts to the GL; the credit shows APPLIED.
  • Canceling a supplier credit now actually reverses it (was a silent no-op).

Future additions

  • Tax routing: line totals (incl. tax) are credited back to the line GL account rather than a separate input-tax account, to avoid re-resolving per-line tax accounts. If precise input-tax reversal is needed, resolve the input-tax account per line as the supplier-invoice posting does.
  • Standalone (unapplied) credits: a credit with no supplier_invoice_id posts to the GL but stays UNAPPLIED with due_amount = total; applying it to an invoice later is not yet wired.