Skip to content

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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, ...)
(web call at 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) / web invoice_print, exportToPdf, printPaymentDetailsSlip. Before printing it ensures the invoice is KRA-signed (wa_esd_details.cu_invoice_number present); if signing is still in progress it returns a retry status. Renders admin.pos_cash_sales.pdq_print and streams for the tablet's Bluetooth thermal printer. Increments print_count.
  • Dispatch / packing slip: dispatchSheet / displayDispatchSheet (:1369) group items by warehouse bin (is_display 0 vs 1). If NetworkDispatchPrintService::isEnabled(), prints over the network to a warehouse printer; otherwise streams PDF. Increments dispatch_print_count.
  • A public, no-login e-receipt exists: GET public/e-receipt/{payment_code} (PublicEreceiptController), keyed by the sale's random payment_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) lists completed sales that still have items in dispatching status for the dispatcher's bin. Setting activate-strict-pos-dispatch-flow forces the dispatcher to scan/enter the exact sales_no and a collected_by name (:3376, :3435).
  • Mark dispatched: process_dispatch (:3442) and QR variant qrDispatch (:3515) set each wa_pos_cash_sales_dispatch row to dispatched, stamping dispatched_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 status Completed, no rows still dispatching, and not already closed, then sets dispatched_at, dipatched_by (sic — column is misspelled), loader_id, is_suspended=1, flips dispatch rows to Collected, 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 API PosCustomerController@addPosCustomer) as wa_route_customer rows on a dedicated is_pos_route route, anchored to a "parent" customer — not into the pos_customers table (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, under PaymentReconciliationController).
  • Cashier Management — CounterCashBalanceTrackingService blocks cashiers holding excess cash; cash drops, CDM, supervisor unblock and OTP feed counter_cash_balance_trackers; pos_cash_payments covers cash payouts/allocations.
  • KRA / eTIMS / ESD tax — every completed CIV is signed via KraVscuSigningService (eTIMS) or EsdSigningService, gating receipt printing.
  • Payments / M-Pesa & SEVI — PosPaymentService + MpesaService (STK push/callback); SeviPaymentMethodAnnotator / SeviOrderSettlementController for 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)

  1. "Receipt scanning" semantics. Inferred from code that a dispatcher "scanning a receipt" = scanning the sales_no barcode/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.
  2. wa_pos_cash_sales_new* tables. Inferred to be an unused alternate design. Confirm no flavor/tenant actually writes to them.
  3. pos_customers / App\PosCustomer. No :: usage found. Confirm it's fully dead (the chapter brief lists PosCustomer as in-scope — it appears the real POS customer is a wa_route_customer on the is_pos_route). Confirm which is authoritative for each flavor.
  4. wa_cash_sales / WaCashSales. Inferred legacy. Confirm it's not still used by any flavor for a different (route/shift) cash-sale style.
  5. Full status vocabulary of wa_pos_cash_sales.status. Confirmed values seen: PENDING, Completed, Archived. The models/schema agent speculated draft/closed/dispatched/received/returned — these were not proven as status values (closure/dispatch is tracked by dispatched_at + dispatch-row status, not the header status). Confirm the exhaustive set.
  6. cash_sale_entries (CashSaleEntry) — purpose unverified (model imported by controller but role unclear). Confirm.
  7. 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.
  8. is_suspended = 1 on close — set in close(); exact downstream meaning (banking hold?) not traced. Confirm.
  9. dipatched_by misspelling — real column name in wa_pos_cash_sales; confirm it's intentional/consistent (used in close and getUserScannedCashSales).
  10. WaEsdDetails signing modes — chapter assumes KRA VSCU (eTIMS) vs ESD KeyGen per branch; confirm which flavors use which today.
  11. Line-number drift — line cites for PosCashSaleService, PerformPostSaleActions, and the API store tail 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.