Credit Notes
Location: dg_smart_pos_api/modules/CreditNotes (the directory has no space; the stub’s Credit Notes was a typo). Owns credit notes: returning one or more line items from an already-paid sale and resolving the credit as a refund (cash/M-Pesa), an exchange for other stock, or a mix of both. It coordinates with Inventory to put returned stock back (InventoryAdjustmentService, InventorySerialNumberService) rather than owning stock itself, and with ReceiptSettings to print the credit note receipt.
Data model
Section titled “Data model”CreditNote:store,sale_id(the original sale),credit_note_number,status(CreditNoteStatusenum:resolved/voided),reason,credit_total,refund_cash/refund_mpesa/refund_total,exchange_total,additional_payment_cash/additional_payment_mpesa(customer pays the difference when exchange value exceeds credit),balance_refunded. Has alegacy_sale_idcolumn alongsidesale_id, and a polymorphicreceipts()relation intoModules\ReceiptSettings\Models\Receipt.CreditNoteItem: one returned line:credit_note_id,stock_id,original_sale_stock_id(links back to thenew_sale_stocksrow it was sold on),quantity,unit_price,broker,serial_numbers(array), tax fields (tax_type_code,tax_rate,taxable_amount,tax_amount).CreditNoteResolution: one resolution line:type(ResolutionTypeenum:refund/exchange), and either refund fields (refund_amount,refund_method,refund_ref) or exchange fields (stock_id,quantity,unit_price).
Creating a credit note
Section titled “Creating a credit note”CreditNoteController::store (StoreCreditNoteRequest) takes reason, return_items[] (stock_id, optional sale_line_id, quantity, unit_price, optional broker/serials[]), and resolutions[] (type: refund|exchange; refund needs refund_amount+refund_method; exchange needs stock_id+quantity+unit_price), plus optional additional_payment_cash/additional_payment_mpesa for when an exchange costs more than the credit.
CreditNoteService::createCreditNote runs the whole thing in one transaction: rejects the sale if it isn’t SaleStatus::Paid or doesn’t belong to the target store, validates returned quantities don’t exceed what was actually sold, computes credit_total from the return items and exchange_total/refund_total from the resolutions, and enforces that if exchange value exceeds credit the caller supplied enough additional_payment_* to cover the difference (throwing otherwise). GenerateCreditNoteReceiptJob is queued afterward to print the credit note receipt via ReceiptService.
Reading and voiding
Section titled “Reading and voiding”index lists credit notes for a store with from_date/to_date/status/pagination filters. show returns one with items/resolutions loaded. void (CreditNoteService::voidCreditNote) flips status to voided after confirming the credit note belongs to the given store.
Route surface
Section titled “Route surface”All under auth:sanctum, prefix credit-notes/stores/{store}, registered via modules/CreditNotes/Routes/credit_notes.php (required once from routes/tenant.php, no duplicate registration found for this module):
| Method | Path | Controller method |
|---|---|---|
| GET | credit-notes |
index |
| GET | credit-notes/{creditNote} |
show |
| POST | sales/{sale}/credit-notes |
store |
| POST | credit-notes/{creditNote}/void |
void |
Related
Section titled “Related”- Sales: credit notes are issued against a
Sale; see the caution above on the parallel legacy return-and-replace flow - Inventory: returned stock and serials go back through
InventoryAdjustmentService/InventorySerialNumberService - ReceiptSettings: credit note receipt generation and printing
- Approvals: not itself an approvable action in
config/approvals.php, unlikesales.void/sales.discount