POS / Cash Sales¶
Chapter scope: How an over-the-counter, walk-in / cash sale gets captured on a POS tablet, paid for, receipted, dispatched (goods handed over), and recorded in the books — all handled inside the bizwiz backend. The tablet-facing app itself (
../bizwizpos, Flutter) is only referenced to explain what it sends; this chapter documents the backend.Out of scope (see other chapters): credit / route delivery orders → Order Taking & Sales Invoicing; banking of the cash collected → Banking & Receivables Reconciliation; the customer master record → Customer Management.
1. Purpose (plain language)¶
A distributor's depot has a physical counter (a "shop") where walk-in buyers turn up, pick goods, pay cash (or M-Pesa / card), and carry the goods away. POS Cash Sales is the part of Bizwiz that captures those over-the-counter transactions.
The typical shape of the flow:
- A cashier on a tablet (or on the web back office) picks the customer, adds items to a basket, and either takes payment immediately or saves the sale as pending.
- Payment is taken as cash, M-Pesa STK push, card, or other tender. Once paid, the sale becomes a Cash Invoice ("CIV") — a real, KRA-signed tax invoice.
- The system prints a receipt for the customer (thermal / Bluetooth printer on the tablet, or PDF) and a dispatch/packing slip telling the warehouse which bins to pick from.
- A dispatcher / loader in the warehouse pulls the goods bin-by-bin, and marks the sale dispatched, often by scanning the sale number (barcode/QR) off the printed slip. When everything is handed over, the sale is closed.
- Behind the scenes, stock is reduced, the sale is posted to the general ledger, the invoice is signed for KRA VAT (eTIMS / ESD), and the customer may get an SMS/WhatsApp receipt.
The module also covers returns of POS goods (with cashier + supervisor approval and OTP), cashier cash management (blocking a cashier who is holding too much un-banked cash), and POS customers (lightweight walk-in customer records).
The key mental model: a POS cash sale is stored in wa_pos_cash_sales and moves through statuses PENDING → Completed → dispatched → closed. When it completes it also spawns an internal requisition (the vehicle for stock-out and GL posting) reusing the same document number.
2. Users & roles (+ permissions)¶
Permissions are per-module slugs of the form module___action checked via $my_permissions[...] or the can() helper. Superadmin (role_id == config('app.allowed_role'), effectively role 1) bypasses all checks.
| Role (functional) | What they do | Key permission slugs |
|---|---|---|
| Tablet cashier | Captures walk-in sales on the Flutter POS app via the API. Sale flagged is_tablet_sale = 1. |
JWT-authenticated API; blocked if declared (see below) |
| Counter / web cashier | Same capture flow from the web back office (PosCashSalesController). Becomes attending_cashier. |
pos-cash-sales___add, pos-cash-sales___view |
| Dispatcher / loader | Picks goods per warehouse bin, marks items dispatched, scans/closes sales. Scoped to their bin (wa_unit_of_measures_id). |
pos-cash-sales___dispatch-progress, pos-cash-sales___dispatch-slip, pos-cash-sales___dispatch-sheets |
| Chief cashier / supervisor | Approves POS returns (esp. over-limit), unblocks cash-blocked cashiers, manages cash drops. | pos-return-list___return-accept, cashier-management___transactions, cashier-management___view, approver-limit-returns___pos-returns, unblock-excess-cash-blocked-cashiers |
| Superadmin / admin | Manual sale completion, resign invoices, view all branches/cashiers, archive. | pos-cash-sales___view-all, pos-cash-sales___archive, pos-cash-sales___re-print, pos-cash-sales___process-bank-overpayment |
Full permission set found in views/controller (resources/views/admin/includes/sidebar_includes/sales_and_receivables.blade.php, PosCashSalesController.php):
pos-cash-sales___{add, view, view-all, print, re-print, pdf, return, return-list,
archive, archived-orders, delayed-orders, dispatch-slip, dispatch-sheets,
dispatch-progress, promotions, process-bank-overpayment}
pos-cash-sales-r___{add, print, re-print, pdf, return} # "returns" sub-module
pos-return-list___return-accept
cashier-management___{view, view-all-cashiers, transactions, cash-banking-report, pos-payments-consumption}
Guardrails that gate who may sell:
- Cashier declaration — a cashier who has "declared" for the day (a CashierDeclaration row for today) is blocked from making any sale (web: PosCashSalesController@store:1123; API: Api/CashSalesController@store:261). Customer is redirected to the chief cashier.
- Counter cash blocking — if a cashier is holding cash over their limit, the counter is blocked until they do a cash drop / bank / get OTP-unblocked (tracked in counter_cash_balance_trackers; see §5 and §6).
3. Processes (start → finish)¶
3.1 End-to-end lifecycle¶
flowchart TD
A[Cashier picks customer + items on tablet/web] --> B{Pay now?}
B -- Save only --> C[Sale saved status=PENDING, sales_no = S-O placeholder]
B -- Cash/Card --> D[recordSale paid=true]
B -- M-Pesa STK --> E[initiatePayment: STK push to phone]
C --> C2[Later: edit / initiatePayment to collect payment]
E --> F[Customer enters M-Pesa PIN]
F --> G[M-Pesa callback: paymentCallback]
D --> H[postPay]
G --> H
H --> I[Status = Completed, paid_at set, CIV number assigned]
I --> J[Create Internal Requisition APPROVED reusing sales_no]
I --> K[Record payments -> wa_pos_cash_sales_payments + GL]
I --> L[Sign invoice KRA eTIMS / ESD]
I --> M[Queue PerformPostSaleActions job]
M --> N[Stock moves -, GL posting, SMS/WhatsApp receipt]
I --> O[Print customer receipt + dispatch slip]
O --> P[Dispatcher picks goods per bin, marks dispatched]
P --> Q[All bins done -> scan sales_no -> close]
Q --> R[dispatched_at set, dispatch rows = Collected, SMS to customer]
3.2 Sale capture (the write path)¶
Both the web (PosCashSalesController@store, line 1039) and the mobile API (Api/CashSalesController@store, line 209) validate the request and then delegate to the same service method:
PosCashSaleService::recordSale($items, $userId, $products, $sales_no,
$route_customer, $payment_methods, $paid, $attending_cashier, ...)
PosCashSalesController.php:1821; API call at Api/CashSalesController.php:~504.)
Inside recordSale (app/Services/PosCashSaleService.php):
- Creates / updates the wa_pos_cash_sales header — sales_no, branch_id, user_id (creating cashier), attending_cashier, is_tablet_sale, and customer fields copied from the route customer (PosCashSaleService.php:110-132).
- Deletes & recreates wa_pos_cash_sales_items lines — qty, selling price, VAT, discounts, cost, and UOM fields when UOM-based inventory is on; expands hampers; marks promotion reward lines (:145-439).
- Sets status = Completed if $paid, else PENDING (:442).
- If paid → calls postPay().
Validation highlights on capture:
- Stock availability check against QoH (blocks overselling).
- Duplicate-sale guard — same customer, same day, identical item/qty fingerprint is rejected (setting CATCH_DUPLICATE_CASH_SALES; API store:271-313). An OTP override exists (sendDuplicateSaleOTP).
- Max selling quantity cap per route customer.
- Supermarket OTP and drop-limit OTP flows for special cases (sendSupermarketOTP, sendDropLimitOTP).
- SEVI (deferred payment) eligibility check.
3.3 Number series: S-O → CIV¶
Paid sales get a placeholder S-O (sales order) number first and are converted to a real CIV (Cash Invoice) number just before postPay finalises them (PosCashSaleService.php:~516-531; NumberSeriesGeneratorService). This "late CIV generation" avoids burning invoice numbers on abandoned/unpaid baskets. Admin can completeSaleManually for stuck CIV sales and resignSale to re-sign an already-completed invoice.
3.4 Payment (M-Pesa STK example)¶
sequenceDiagram
participant Tab as POS Tablet
participant API as Api/CashSalesController
participant Pay as PosPaymentService
participant Mpesa as MpesaService
Tab->>API: POST cash-sale-initiate-payment (sales_id, phone, amount)
API->>API: recalc amount from items, guard already-complete
API->>Pay: initiatePayment(sale, total, method, phone)
Pay->>Mpesa: stkPush() (creates InvoicePayment status=pending)
Mpesa-->>Tab: STK prompt on phone
Tab->>API: GET cash-sale-verify-payment (checkout_request_id) [poll]
Mpesa-->>Pay: callback paymentCallback()
Pay->>Pay: mark InvoicePayment settled
Pay->>API: PosCashSaleService::postPay(sale) -> Completed
Tab->>API: GET cash-sale/{id}/check-is-complete
API-->>Tab: completion payload (CIV + payments recorded)
Key: PosPaymentService::initiatePayment (:15-68), paymentCallback (:70-122, calls postPay at :102). Cash/card sales skip the STK loop and complete directly inside store.
3.5 Post-pay: recording the sale (postPay)¶
PosCashSaleService::postPay (:1355-1456) runs once (guarded against double-processing) and:
1. Builds dispatch rows (WaPosCashSalesDispatch, one per item, mapped to warehouse bins) — dispatch() at :1370.
2. Sets status = Completed, paid_at = now().
3. Creates a WaInternalRequisition (status APPROVED) with requisition_no = sales_no and a lowercase slug (civ...) so downstream jobs recognise it as a cash-sale (:1390-1408), plus WaInternalRequisitionItem lines.
4. Signs the invoice for KRA (signInvoice()).
5. Records payments → wa_pos_cash_sales_payments (change calc, tender-entry consumption, GL) via payments() (:980-1200).
6. Queues PerformPostSaleActions + SaveBasketSale.
PerformPostSaleActions (app/Jobs/PerformPostSaleActions.php) then does the heavy accounting:
- Stock moves — negative wa_stock_moves rows; hamper components decremented (:214-365).
- GL posting — via ExtensiveGlPostingService (if enabled) or standard posting: debit Cash (CIV) / Debtors, credit Sales, credit VAT control (:435-670).
- KRA/ESD signing fallback (:672-690).
- Customer notification — SMS/WhatsApp with invoice + payment paybill details (:732-939).
- CIV cash sales skip debtor (A/R) recording and customer-visit marking (identified by slug starting civ).
3.6 Receipt & dispatch printing¶
- Customer receipt:
Api/CashSalesController@customerReceipt(:1243) / webinvoice_print,exportToPdf,printPaymentDetailsSlip. Before printing it ensures the invoice is KRA-signed (wa_esd_details.cu_invoice_numberpresent); if signing is still in progress it returns a retry status. Rendersadmin.pos_cash_sales.pdq_printand streams for the tablet's Bluetooth thermal printer. Incrementsprint_count. - Dispatch / packing slip:
dispatchSheet/displayDispatchSheet(:1369) group items by warehouse bin (is_display0 vs 1). IfNetworkDispatchPrintService::isEnabled(), prints over the network to a warehouse printer; otherwise streams PDF. Incrementsdispatch_print_count. - A public, no-login e-receipt exists:
GET public/e-receipt/{payment_code}(PublicEreceiptController), keyed by the sale's randompayment_code.
3.7 Dispatcher scan / verify / close¶
flowchart LR
S[Completed sale prints dispatch slip w/ sales_no barcode] --> D1[Dispatcher opens Dispatch screen scoped to their bin]
D1 --> D2{Strict dispatch flow?}
D2 -- Yes --> D3[Must scan/enter exact sales_no + collector name]
D2 -- No --> D4[List sales in date range]
D3 --> P[process_dispatch: mark bin items dispatched]
D4 --> P
P --> QR[Or scan bin QR: qrDispatch saleId/binId]
QR --> P
P --> C{All bins dispatched?}
C -- Yes --> CL[API close: scan sales_no -> dispatched_at, rows=Collected, SMS]
- Dispatch screen:
dispatch_pos(:3345) listscompletedsales that still have items indispatchingstatus for the dispatcher's bin. Settingactivate-strict-pos-dispatch-flowforces the dispatcher to scan/enter the exactsales_noand acollected_byname (:3376,:3435). - Mark dispatched:
process_dispatch(:3442) and QR variantqrDispatch(:3515) set eachwa_pos_cash_sales_dispatchrow todispatched, stampingdispatched_by,dispatched_time,dispatch_quantity,collected_by. - Close (goods fully handed over): API
close(Api/CashSalesController@close:1015) — the dispatcher scans the sale number; backend verifies statusCompleted, no rows stilldispatching, and not already closed, then setsdispatched_at,dipatched_by(sic — column is misspelled),loader_id,is_suspended=1, flips dispatch rows toCollected, and SMS-notifies the customer.getUserScannedCashSales(:1691) lists what a dispatcher closed today.
Note on "receipt scanning": in this backend, "scanning" means the dispatcher scanning the sales number barcode/QR off the printed slip to pull up and close/dispatch the sale — there is no image OCR of a paper receipt. See §7.
3.8 Returns (walk-in goods returned)¶
return_items / return_items_post (:5134, :5182) create wa_pos_cash_sales_items_return rows. Returns go through cashier approval then supervisor/dispatcher acceptance, with OTP on rejects and an over-limit approval ladder (cashierApproval, handleReturnApproval, posReturnAprovals, bulk variants, acceptReturn at :6335). The dispatcher accepts returns scoped to their bin (returned_cash_sales_list_dispatcher:6708), which checks cashier_status = approved and is_approved = true. Signing of return credit notes is queued via SignPosReturns. Late returns have a separate list/approval path.
4. Tables touched & key data¶
Core (current)¶
| Table | Role | Notable columns |
|---|---|---|
wa_pos_cash_sales |
Sale header | sales_no (S-O→CIV), status (PENDING/Completed/Archived), user_id (creating cashier), attending_cashier, is_tablet_sale, branch_id, wa_route_customer_id, customer, customer_pin, customer_phone_number, payment_method_id, cash, change, paid_at, dispatched_at, dipatched_by, loader_id, is_suspended, open_by (edit lock), payment_code (unique, for public e-receipt), print_count, dispatch_print_count |
wa_pos_cash_sales_items |
Sale lines | wa_pos_cash_sales_id, wa_inventory_item_id, qty, selling_price, vat_percentage, vat_amount, tax_manager_id, discount_percent, discount_amount, standard_cost, is_dispatched, is_return, return_quantity; UOM: uom_id, uom_quantity, uom_selling_price, conversion_factor, is_base/second/third_unit; group-promo columns |
wa_pos_cash_sales_payments |
Payment splits | payment_method_id, gl_account_id, amount, wa_tender_entry_id, receipt_no, unique_reference_number (unique), cashier_id, branch_id, verified, reconciled, posted, bank_statement_id |
wa_pos_cash_sales_dispatch |
Per-item picking/handover | pos_sales_id, pos_sales_item_id, wa_unit_of_measure_id (bin), status (dispatching→dispatched→Collected), dispatched_by, dispatched_time, dispatch_quantity, collected_by, desp_no |
wa_pos_cash_sales_items_return |
Returns | wa_pos_cash_sales_item_id, return_by, return_quantity, reason_id, bin_location_id, otp, cashier_status, accepted/accepted_by/accepted_at, rejected, is_approved |
Supporting¶
| Table | Role |
|---|---|
counter_cash_balance_trackers |
Per-cashier cash-in-hand ledger: cashier_limit, due_payouts, cumulated_balance, current_limit, block_counter, action (cash_drop/cdm/supervisor_unblock/otp). Counter blocked when current_limit <= 0. |
blocked_counter_otp |
OTP issuance/verification to unblock a cash-blocked counter. |
pos_cash_payments + pos_cash_payment_documents |
POS cash payouts/allocations (a payment out workflow: initiated_by, payee, amount, status Pending/Approved/Rejected, attached docs). "Cash Payments" menu under Cashier Management. |
pos_missing_items |
Reported missing/lost POS items. |
wa_esd_details |
KRA/ESD signing result per invoice (invoice_number, cu_invoice_number, status). |
wa_internal_requisitions / _items |
Created on completion; the stock-out + GL vehicle (requisition_no = sales_no). |
wa_stock_moves, wa_gl_tran, wa_account_transactions |
Stock reduction and ledger entries from PerformPostSaleActions. |
wa_tender_entries |
Non-cash tender references (M-Pesa/card) consumed by a payment. |
Legacy / vestigial (see §6)¶
wa_cash_sales, wa_cash_sales_item, reversed_cash_sales, wa_pos_cash_sales_new(*), pos_customers, cash_sale_entries.
5. Interactions with other modules¶
- Inventory / Stock — availability (QoH) is checked at capture; completion writes
wa_stock_moves; items map to warehouse bins (wa_unit_of_measure/wa_inventory_location_uom) which drive dispatch scoping. - Customer Management — sales are made against a
wa_route_customer. Walk-in POS customers are created (via APIPosCustomerController@addPosCustomer) aswa_route_customerrows on a dedicatedis_pos_routeroute, anchored to a "parent" customer — not into thepos_customerstable (see §6/§7). KRA-PIN validation raises an approval request. - Order Taking & Sales Invoicing — completion creates an internal requisition reusing the sale number; both modules share
NumberSeriesGeneratorService, hampers, promotions, and the post-sale job. - Banking & Receivables Reconciliation — cash & tender collected here feeds cash-sale banking,
wa_pos_cash_sales_payments.verified/reconciled/posted, "Cash Banking Report", and the Reconciliation menu (Payment Verifications/Approvals live in that chapter, underPaymentReconciliationController). - Cashier Management —
CounterCashBalanceTrackingServiceblocks cashiers holding excess cash; cash drops, CDM, supervisor unblock and OTP feedcounter_cash_balance_trackers;pos_cash_paymentscovers cash payouts/allocations. - KRA / eTIMS / ESD tax — every completed CIV is signed via
KraVscuSigningService(eTIMS) orEsdSigningService, gating receipt printing. - Payments / M-Pesa & SEVI —
PosPaymentService+MpesaService(STK push/callback);SeviPaymentMethodAnnotator/SeviOrderSettlementControllerfor deferred-payment (SEVI) settlement (pos-cash-sales/{id}/sevi-initiate). - Notifications — SMS (
InfoSkySmsService) + WhatsApp receipts on completion and on close. - Promotions & Discounts — group promotions, discount bands, discount codes, and earned-discount payments applied during capture (
checkGroupDiscountPromotions,validate-discount-code,calculateInventoryItemDiscount).
6. Alternatives & variants¶
Feature flags / settings
- activate-strict-pos-dispatch-flow — forces dispatchers to scan the exact sales_no + capture collector name before dispatching (dispatch_pos:3376).
- CATCH_DUPLICATE_CASH_SALES — enables the duplicate-sale guard (with OTP override).
- PRINT_CASH_SALE_DISPATCH_SEPARATELY — shows the separate "Print Dispatch Slip" menu.
- INVENTORY_SETUP = 'UOM BASED' (AdministrationSetting) — switches the whole flow to UOM quantities/prices (uom_quantity, uom_selling_price, conversion factors).
- ALLOW_KRA_PIN_VALIDATION — POS customer PINs require metadata + approval.
- NetworkDispatchPrintService::isEnabled() — network warehouse printing vs PDF/Bluetooth.
- eTIMS-per-branch (ElectronicTaxInvoicingService::isETimsForBranch) — KRA VSCU vs older ESD signing.
- Supermarket branches (wa_location_and_store.is_supermarket) — extra OTP + display-bin dispatch behaviour.
Capture channels
- Mobile/tablet (Api/CashSalesController, JWT) — sale flagged is_tablet_sale=1; includes edit-lock (startEditing/finishEditing via open_by).
- Web back office (PosCashSalesController) — same recordSale, attending_cashier = logged-in user.
Legacy / dormant (do not treat as live):
- wa_cash_sales / wa_cash_sales_item (with cash_sales_number, shift_id, request_or_delivery) look like an older cash-sales design superseded by wa_pos_cash_sales.
- wa_pos_cash_sales_new(*) tables + WaPosCashSalesNew* models are a thin, apparently unused alternate structure (see §7).
- App\PosCustomer (pos_customers: just name, phone_number) has no :: references anywhere in the codebase — effectively vestigial; real POS customers are wa_route_customer rows.
- cash_sale_entries (CashSaleEntry) is an empty/minimal model with no clear active use.
- PosCashSalesTestController + pos-cash-sales-test/* routes are a test/staging clone of the flow.
- Older customerView_OLD and commented-out merge/mpesa routes indicate iterative refactors.
7. Open questions to confirm (inferences)¶
- "Receipt scanning" semantics. Inferred from code that a dispatcher "scanning a receipt" = scanning the
sales_nobarcode/QR off the printed dispatch/customer slip to pull up and close the sale (close,qrDispatch, strict dispatch flow). Confirm there is no separate paper-receipt image capture/OCR step. wa_pos_cash_sales_new*tables. Inferred to be an unused alternate design. Confirm no flavor/tenant actually writes to them.pos_customers/App\PosCustomer. No::usage found. Confirm it's fully dead (the chapter brief listsPosCustomeras in-scope — it appears the real POS customer is awa_route_customeron theis_pos_route). Confirm which is authoritative for each flavor.wa_cash_sales/WaCashSales. Inferred legacy. Confirm it's not still used by any flavor for a different (route/shift) cash-sale style.- Full status vocabulary of
wa_pos_cash_sales.status. Confirmed values seen:PENDING,Completed,Archived. The models/schema agent speculateddraft/closed/dispatched/received/returned— these were not proven asstatusvalues (closure/dispatch is tracked bydispatched_at+ dispatch-row status, not the header status). Confirm the exhaustive set. cash_sale_entries(CashSaleEntry) — purpose unverified (model imported by controller but role unclear). Confirm.pos_cash_payments— documented as a cash pay-out/allocation workflow ("Cash Payments" / "Payment Allocations" menu), distinct from sale receipts. Confirm this is money out (e.g. reimbursements) vs money in.is_suspended = 1on close — set inclose(); exact downstream meaning (banking hold?) not traced. Confirm.dipatched_bymisspelling — real column name inwa_pos_cash_sales; confirm it's intentional/consistent (used incloseandgetUserScannedCashSales).WaEsdDetailssigning modes — chapter assumes KRA VSCU (eTIMS) vs ESD KeyGen per branch; confirm which flavors use which today.- Line-number drift — line cites for
PosCashSaleService,PerformPostSaleActions, and the APIstoretail come from sub-agent reads; spot-verify before publishing as they may drift a few lines.
8. Source references¶
Navigation / IA
- resources/views/admin/includes/sidebar_includes/sales_and_receivables.blade.php — "Cash Sales" tree (:1095-1330), Cashier Management submenu (:1180-1272), Reconciliation (Payment Verifications/Approvals) (:754-801).
Routes
- routes/web.php:1257-1330 — pos-cash-sales.* (resource + dispatch, returns, QR, OTP, discounts, SEVI). :1500-1509 — pos-cash-sales-test.* (test clone).
- routes/api.php:912-956 — cash-sale* mobile endpoints (store/update/close/receipts/dispatch-sheet/initiate-payment/verify). :734-735 — POS customer add/list. :1362 — public e-receipt.
- routes/modules/sales_and_receivables.php:563-564 — counter-sales-transactions report; :595-599 — unbalanced-cash-sales report; :549 — add POS customer from web portal.
Controllers
- app/Http/Controllers/Admin/PosCashSalesController.php — web/back-office + dispatcher: index:127, store:1039, update:2413, dispatch_pos:3345, process_dispatch:3442, qrDispatch:3515, customerView:3703, completeSaleManually:5004, return_items(_post):5134/5182, acceptReturn:6335, returned_cash_sales_list_dispatcher:6708, resignSale:8009, OTP + blocked-counter methods :3165-3308.
- app/Http/Controllers/Api/CashSalesController.php — tablet API: store:209, update:570, startEditing/finishEditing:844+, close:1015, customerReceipt:1243, dispatchSheet:1369, initiatePayment:~1513, verify:1672, getUserScannedCashSales:1691, checkIsComplete:533, getPaymentMethods:1075.
- app/Http/Controllers/Admin/PosCustomerController.php:77 (addPosCustomer → creates wa_route_customer), :52, :181.
Services & jobs
- app/Services/PosCashSaleService.php — recordSale:62, payments:980, dispatch:1304 / ensureDispatchRowsForPosItems:1274, postPay:1355.
- app/Services/PosPaymentService.php — initiatePayment:15, paymentCallback:70.
- app/Services/CounterCashBalanceTrackingService.php — cash tracking/blocking :14-165.
- app/Services/PosCashSaleCompletionGuard.php:24, PosCashSaleCompletionLock.php.
- app/Jobs/PerformPostSaleActions.php — stock/GL/signing/notify :57-1027; app/Jobs/SignPosReturns.php.
Models
- app/Model/WaPosCashSales.php, WaPosCashSalesItems.php, WaPosCashSalesPayments.php, WaPosCashSalesDispatch.php, WaPosCashSalesItemReturns.php, WaPosCashSalesNew.php, WaCashSales.php, ReversedCashSale.php.
- app/PosCustomer.php; app/Models/PosCashPayment.php, PosCashPaymentDocument.php, CounterCashBalanceTracker.php, BlockedCounterOtp.php, PosMissingItem.php, CashSaleEntry.php.
- app/Enums/Status/CashSaleDispatchStatus.php — dispatching / dispatched / Collected.
Migrations (database/migrations/)
- 2023_09_08_134414_create_wa_pos_cash_sales_table.php (+ _items, _dispatch, _items_return, _new*).
- 2024_02_16_..._create_pos_customers_table.php, 2024_05_02_..._create_cash_sale_entries_table.php.
- 2024_08_15_..._add_is_tablet_sale_flag_to_pos_cash_sales.php, 2024_05_23_..._add_attending_cashier..., 2024_05_20_..._add_paid_at....
- 2024_11_14_..._create_pos_cash_payments_table.php (+ pos_cash_payment_documents).
- 2025_07_08_..._create_counter_cash_balance_trackers_table.php, 2025_07_09_..._create_blocked_counter_otp_table.php.
- 2025_11_11_..._add_uom_support_to_wa_pos_cash_sale_items.php, 2024_11_05_..._add_verification_status_to_wa_pos_cash_sales_payments.php, 2024_09_23_..._add_otp_field_to_wa_pos_cash_sales_items_return_table.php.