Banking & Receivables Reconciliation¶
Chapter scope: the "Banking" and "Reconciliation" nav areas under Sales & Receivables. This is the money-in engine: how money collected in the field (route sales), at POS (cash sales), and from debtors (credit customers) gets matched to bank statements, verified, approved, posted to the GL, and how exceptions (unknown/unmatched/short/suspended) are handled.
Out of scope (owned by other chapters): how the sale/invoice/cash sale is created (Order Taking, POS/Cash Sales), customer credit standing & bad debts (Customer Management), accounts payable / money-out and bank-file generation (Procurement/Finance), and the deep GL chart-of-accounts/journals (Finance & Accounting). This chapter owns the reconciliation and banking-approval side and documents the handoff point where money-in transactions post into the GL.
1. Purpose (plain language)¶
Every day, Bizwiz distributors take in money three ways:
- Route sales — salesmen sell to shops on their routes; customers pay by M-Pesa, bank transfer (Equity/KCB/Coop), Vooma, Eazzy, cheque, or cash that is later banked.
- Cash sales at the counter (POS) — walk-in sales, with cash dropped and banked, plus digital channel payments.
- Debtor payments — credit customers pay down invoices later.
The problem this module solves: the money the ERP thinks it received has to be reconciled against what the bank actually received. Salesmen key in a receipt ("customer paid 5,000 via M-Pesa, ref ABC123"); separately, the bank statement shows a deposit ("5,000, ref ABC123"). Reconciliation matches the two, an approver signs off, and only then is the receipt posted to the General Ledger as real money in the bank account.
The workflow, at a glance:
- Upload the bank statement (per channel).
- Match each statement line to a system receipt (auto-match on reference + amount + date, or manual allocation).
- Verify the matched pairs.
- Approve the verified batch — this is the point where GL journal entries are written (debit bank / credit debtors-control or cash-control).
- Close the day's banking.
Anything that doesn't match cleanly is routed to an exception queue:
- Unknown Bankings — money hit the bank but no system receipt claims it. Someone must "claim" it and an approver resolves the claim.
- Unmatched Debtors — a receipt exists but isn't attached to a customer/invoice yet.
- Short Banking / Excess — the amount banked is less (or more) than expected.
- Suspended → Expunged / Restored — a receipt is pulled out of the workflow (wrong customer, duplicate, fraud), then either permanently voided (expunge) or re-created with corrected details (restore).
The two "Banking" dashboards (Route Sales Banking, Cash Sales Banking) are the operational front-ends where verify/approve/close happens; the "Reconciliation" menu holds the statement-level tooling (verifications, matching records, approvals, statements, logs, debtor tools).
2. Users & roles (permissions)¶
Access is gated by the sales-and-receivables___view top-level permission plus fine-grained reconciliation___* and sibling permissions. Users with role_id == config('app.allowed_role') (super-role) bypass the checks. Menu gating is in resources/views/admin/includes/sidebar_includes/sales_and_receivables.blade.php:709-898.
Permission strings are checked in code as can('<action>', 'reconciliation') (module reconciliation), e.g. can('see-overview', 'reconciliation'). In the sidebar they render as reconciliation___<action>.
Banking area¶
| Menu item | Route name | Permission | Controller |
|---|---|---|---|
| Route Sales Banking | route-banking-approval.overview |
reconciliation___see-overview-route |
BankingApprovalController@showRouteOverviewPage (app/Http/Controllers/BankingApprovalController.php:49) |
| Cash Sales Banking | pos-banking.daily-overview |
reconciliation___see-overview-pos |
PosBankingController@showDailyOverviewPage (app/Http/Controllers/PosBankingController.php:91) |
| GL Reconciliation | gl-recon.overview |
reconciliation___gl-reconciliation |
GlReconciliationController@showOverviewPage (app/Http/Controllers/GlReconciliationController.php:22) |
| Unknown Bankings | unknown-bankings.index |
reconciliation___unknown-bankings |
UnknownBankingsController@index (app/Http/Controllers/Admin/UnknownBankingsController.php:39) |
| Banking Utility | banking-utility.index |
reconciliation___banking-utility |
BankingUtilityController@index (app/Http/Controllers/Admin/BankingUtilityController.php:45) |
The Banking parent menu itself requires reconciliation___see-overview (sales_and_receivables.blade.php:709).
Reconciliation area¶
| Menu item | Route name | Permission |
|---|---|---|
| Payment Verifications | payment-reconciliation.verification |
reconciliation___verification |
| Matching Records | payment-reconciliation.matching-records |
matching-records___view |
| Payment Approvals | payment-reconciliation.approval |
reconciliation___approval |
| Suspended / Expunged / Restored Transactions | suspended-transactions.{index,expunged,restored} |
reconciliation___suspend |
| Debtor Transactions | debtor-trans |
reconciliation___view-debtor-trans (multi-branch adds view-branches-debtor-trans) |
| Debtor Transfers | debtor-transfers.index |
reconciliation___transfer-debtor-record |
| Unmatched Debtors | unmatched-debtors.index |
unmatched-debtors___view (matching action: match-unmatched-debtors) |
| Bank Statements (Listing / Channel Statements) | bank-statements, bank-statements.channel-statements |
reconciliation___bank-statement-upload |
| Approve Uploads | manual-upload-list |
reconciliation___view-manual-upload |
| Bank Posting Logs | bank-posting-logs |
reconciliation___bank_post_log |
| Bank Error Logs | bank-error-logs |
reconciliation___bank-error-logs |
| Transaction History | transaction-history |
reconciliation___transaction-history |
| View Invoice Payments | view-invoice-payments.index |
reconciliation___view-invoice-payments |
The Reconciliation parent menu requires reconciliation___view (sales_and_receivables.blade.php:754).
Action-level permissions (used inside controllers)¶
reconciliation___post-sweeps-to-gl— post bank "sweep" transfers to GL (BankingApprovalController@postSweepsToGL).reconciliation___claim-unknown— claim an unknown banking (UnknownBankingsController@claim).reconciliation___resolve-unknown-claims— approve/reject unknown-banking claims.reconciliation___view-all-branches— see all branches instead of only the user's own (POS banking, banking utility, debtor trans).reconciliation___allocate-old-payments,reconciliation___allocate-payments-from-main-accounts,reconciliation___unverify-cash-sales— POS allocation/undo controls.reconciliation___edit-debtor-reference,reconciliation___approve-debtor-edit-request— debtor reference editing / approval.
Note: the sidebar wires a few Route-Approval links to
reconciliation___*permissions (e.g.sales_and_receivables.blade.php:464,471,477). This looks like copy-paste reuse of permission strings rather than a real dependency — see Open Questions.
3. Processes (start → finish)¶
3.1 The core reconciliation state machine¶
The central ledger is a verification range (payment_verifications) with two sides:
- System side (
payment_verification_systems) — the ERP's own receipts (fromwa_debtor_trans, the debtor-transaction table). - Bank side (
payment_verification_banks) — the uploaded bank-statement lines.
The status enum driving both sides is App\Enums\Status\PaymentVerification (app/Enums/Status/PaymentVerification.php): Verifying, Processing, Partially Verified, Verified, Pending, Approved, Partially Approved, Discard, Duplicate, Same Reference.
The receipt itself (wa_debtor_trans) carries its own lifecycle columns: verification_status (pending → verified → approved, plus manual upload), bank_statement_id (link to the matched bank line), posting_date (set once posted to GL), and flags is_settled, reconciled, manual_upload_status.
stateDiagram-v2
[*] --> Unmatched: receipt created (wa_debtor_trans)\nOR statement line uploaded (payment_verification_banks)
Unmatched --> Matched: auto-match on reference+amount+date\nOR manual allocation
Matched --> Verified: verify (verification_status=verified,\nbank line status=Verified)
Verified --> Approved: approve batch\n(verification_status=approved)
Approved --> Posted: GL journal written\n(posting_date set, gl_posting_logs row)
Posted --> [*]
Unmatched --> Suspended: pull out of workflow\n(wrong cust / dup / fraud)
Matched --> Suspended
Verified --> Suspended
Suspended --> Expunged: permanent void\n(GL/bank/tender rows deleted)
Suspended --> Restored: re-create with edited\ncustomer/reference/amount
Restored --> Unmatched: new wa_debtor_trans re-enters flow
Expunged --> [*]
Unmatched --> UnknownBanking: bank line with no system receipt
UnknownBanking --> Claimed: user claims (claim-unknown)
Claimed --> ResolvedUnknown: approver resolves (resolve-unknown-claims)\nis_unknown cleared → available for matching
Claimed --> UnknownBanking: rejected → back to pending
UnknownBanking --> PostedAsUnknown: postUnknowns → dr Unknown GL / cr Bank
3.2 Route Sales Banking approval¶
Controller: app/Http/Controllers/BankingApprovalController.php. A day's banking for a branch is tracked by a banking_approvals row with sales_type = 2 (route). The controller fetches/creates it via getBankingRecord (:109) and progresses it through stages (verifyBanking :932, approveBanking :989, closeBanking :965).
flowchart TD
A[Overview dashboard\nshowRouteOverviewPage] --> B[Daily records\naggregate sales, receipts,\ndebtor trans per channel]
B --> C[Channel breakdown\ngetMpesa/getEazzy/getEbMain/\ngetVooma/getKcbMain/getCoop...]
C --> D{Match receipts\nto bank lines}
D -->|matched| E[verifyBanking\nstage=verified]
D -->|no receipt| F[postUnknowns\nmark bank line is_unknown]
D -->|unverified junk| G[suspendUnverified\n→ SuspendedTransaction + FraudJournal]
E --> H[approveBanking\nstage=approved + WRITE GL]
H --> I[closeBanking\nstage=closed]
H --> J[wa_gl_trans + wa_banktran inserted]
approveBanking (:989) is the GL handoff for route sales. For each wa_debtor_trans being approved (filtered verification_status != 'approved' and document_no not like 'BDT-%', :1006):
- Sets
verification_status = 'approved'. - Writes a credit to Debtors Control for
-abs(amount). - If the document is an RCT receipt: writes a debit to the bank channel account for
+abs(amount). - Otherwise (non-receipt): writes a debit to a Fraud account (
55001-007) for+abs(amount). - Bulk-inserts into
wa_gl_trans(:1092) andwa_banktran(:1093).
suspendUnverified (:853) handles unverified route junk: creates a SuspendedTransaction (status expunged), deletes the tender entry, writes a FraudJournal, creates a replacement wa_debtor_trans on the fraud-journals channel, then deletes the original.
postSweepsToGL (:2751, permission post-sweeps-to-gl): a "sweep" is a bank-to-bank transfer between accounts. Posts a credit to the source bank nominal and a debit to the sweep destination (sweep_channel / paymentMethod->sweep_account_id), marks the sweep Verified, resolves any linked UnknownBanking to resolved, and writes a BankStatementConsumer for traceability.
postUnknowns (:5746): marks bank lines as unknown-in-GL — debits an Unknown/undebited-transactions GL account and credits the bank; sets payment_verification_banks.is_unknown = true, is_posted_in_gl = 1, and creates an unknown_bankings row.
Per-channel record getters (all read payment_verification_banks scoped to route customers): getEazzy :1102, getEbMain :1276, getVooma :1448, getKcbMain :1623, getCoop :1797, getCoopMain :1969, getMpesa :2142. Equity/KCB/Coop each split into a channel account (account_type=1, use_in_pos=0) and a "main" account (account_type=2).
3.3 Cash Sales Banking (POS daily overview)¶
Controller: app/Http/Controllers/PosBankingController.php. Uses a banking_approvals row with sales_type = 1 (POS) and a four-stage lifecycle:
flowchart TD
A[Daily overview\nshowDailyOverviewPage] --> B[getDailyRecords\ncash sales, returns, CDM drops,\npayment breakdowns]
B --> C[runVerification\nmatch POS payments ↔ bank lines\n+ auto-verify CDM drops]
C --> D[completeVerification\nstage=2 verified]
D --> E[approveAndCloseBanking\nstage=3 approved + WRITE GL]
E --> F[closeBanking\nstage=4 closed]
C -.->|manual| G[allocateCdmDeposit / allocateCbDeposit\n+ GlPostingService]
C -.->|short/excess| H[short_bankings_comments\nallocate / markAsFraud]
C -.->|no receipt| I[getUnknown → postUnknowns]
runVerification (:1850): matches each unverified wa_pos_cash_sales_payments to a PaymentVerificationBank (primary: amount + reference + Pending + optional channel; fallback: amount + substring reference). Sets the payment verified=true, bank_statement_id; sets the bank line Verified; writes a BankStatementConsumer. Also auto-verifies CDM drops (banked_drop_transactions) by matching bank references.
approveAndCloseBanking (:6188) is the GL handoff for cash sales. For each verified payment it resolves a Cash Control account (default 54008-000, or branch-specific when SEPARATE_GL_ACCOUNTS_PER_BRANCH is set), then writes: credit Cash Control -abs(amount), and either debit bank (RCT receipt) or debit Fraud (55001-007) (non-receipt), +abs(amount). Bulk-inserts wa_gl_trans + wa_banktran.
CDM (Cash Deposit Machine) deposits: getCdms :2034, getDrops :2231, allocateCdmDeposit :2253 (validates the statement, creates a BankedDropTransaction, finds/creates a tender entry, then posts GL via GlPostingService: credit Cash / debit Bank).
Short banking & excess: getShortBankingRecords :4596 lists short_bankings_comments (amount > 0 = "short", < 0 = "excess"); allocateShortBanking :4557 resolves a short against a bank statement line; markShortBankingAsFraud :7944 resolves it as fraud and writes a FraudJournal.
3.4 Statement upload → matching → verification → approval → posting (Reconciliation menu)¶
Controller: app/Http/Controllers/Admin/Finance/PaymentReconciliationController.php; routes in routes/web.php:5679-5700.
flowchart TD
A[verification_create\nnew payment_verifications range] --> B[verification_upload\nparse Excel/CSV bank statement\n→ payment_verification_banks Pending]
B --> C[verification_process\nmatch bank lines ↔ wa_debtor_trans\non reference+amount+date +/- channel]
C --> D[Verified\nbank line=Verified, debtor=verified\n+ BankStatementConsumer]
D --> E[approval / approval_store\nverification_status=approved]
E --> F[GL posting job\nApproveReconciliationPayments\n+ gl_posting_logs]
C -.->|no match| G[manual allocation\nManualUploadController]
C -.->|discard| H[verification_discard]
C -.->|suspend| I[verification_suspend]
verification_upload(:531) parses the statement file (columns: trans_date, channel, reference, amount, narration) intopayment_verification_banks(Pending), and can create tender entries.verification_process(:42) joins bank lines towa_debtor_transon reference + amount + date range, usingROW_NUMBER()to force 1:1 matches. The settingMATCH_CHANNEL_ON_PAYMENT_VERIFICATION(:70) controls whether the payment method/channel must also match.matchingRecordsView(:949) is the "Matching Records" screen — shows which statement lines matched which receipts.approval/approval_store(:1347) setsverification_status = approved, queues the GL posting job, and writesgl_posting_logs.bank_post_logs(:1697, routebank-posting-logs) shows GL postings grouped bytransaction_no.
3.5 Manual uploads → approval (Approve Uploads)¶
Controller: app/Http/Controllers/Admin/ManualUploadController.php; routes routes/web.php:5746-5753.
When auto-matching fails, an operator manually allocates a bank statement line to an invoice: manual_upload_transaction (:46) creates a wa_debtor_trans with verification_status='manual upload', a WaTenderEntry, invoice allocation, and a BankStatementConsumer; the bank line becomes Verified. These land in Approve Uploads (manual_upload_list :671, RCT docs with manual_upload_status=1), where manual_update_status (:828) or manual_bulk_approve (:842) promotes them to verified (recording manual_upload_approved_by), after which they can be approved to approved in the normal flow.
3.6 Unknown / Unmatched handling¶
- Unknown Bankings (
UnknownBankingsController): a bank line flaggedis_unknown=1with no matching receipt. Lifecyclepending → claimed → resolved(or back topendingon reject): claim(:532, permissionclaim-unknown) setsclaimed, recordsrequested_by/at+ comment, and SMS-notifies resolvers.approveClaim(:579, permissionresolve-unknown-claims) setsresolved, clearspayment_verification_banks.is_unknown, SMS-notifies the claimer.rejectClaim(:630) returns topendingwith a rejection reason.- Bulk variants:
bulkClaim :688,bulkApproveClaim :759,bulkRejectClaim :838. - Unmatched Debtors (
app/Http/Controllers/Admin/Finance/UnmatchedDebtorsController.php):wa_debtor_transwithwa_sales_invoice_id IS NULL AND wa_route_customer_id IS NULLanddocument_no NOT LIKE 'RCOP%'(:100).match(:165) attaches the receipt to a chosen route customer + invoice after validating the invoice belongs to that customer.
3.7 Suspended → Expunged / Restored¶
Controller: app/Http/Controllers/Admin/SuspendedTransactionController.php; routes routes/web.php:5764-5773.
- Suspend (
store :120): checks the receipt isn't alreadyApproved, creates asuspended_transactionsrow (statussuspended) with the original data + reason, cleans up allocations (invoice_payments_allocations,rcop_allocations,bank_statement_consumers), resets the matched bank line toPending, and deletes thewa_debtor_trans. - Expunge (
expunge :268): sets statusexpunged; deletes thewa_gl_trans,wa_bank_trans, andwa_tender_entryrows for that document — permanent void, audit trail only. - Restore (
restore :305): sets statusrestoredwith edited customer/reference/amount; creates a newwa_debtor_transandWaTenderEntrywith the corrected values, re-mapping the payment method by channel (Eazzy=7, Equity=10, Vooma=8, KCB=9, M-Pesa/Cheque=3,:351).
transaction-history (TransactionHistoryController@fetch) ties this together: search a document_no to see the original wa_debtor_trans plus every suspend/expunge/restore action against it.
3.8 GL Reconciliation (bank vs GL control)¶
GlReconciliationController@getRecords (:83) compares, per channel: bank debits/credits (sum of payment_verification_banks.amount by type) against system debits/credits (sum of wa_banktrans.amount for the bank GL account), starting from a per-channel opening balance (bank_opening_balances, editable via getOpeningBalances/updateOpeningBalances), and surfaces a variance for each. The view (resources/views/gl_recon/overview.blade.php) shows Opening Balance, System Debits/Credits, and Debit/Credit Variance columns.
4. Tables touched & key data¶
Migrations live in database/migrations/. Key tables (with the columns that drive the state machine in bold):
Verification ledger
- payment_verifications (2024_05_13_121816_...) — a reconciliation range: created_by, start_date, end_date, branch_id, channel,status (PaymentVerification enum).
- payment_verification_banks (2024_05_14_104950_...) — bank-statement lines: reference, amount, bank_date, channel, payment_method_id,status(Pending→Verified→Approved),matched_debtors_id(→ wa_debtor_trans = matched),is_unknown, is_posted_in_gl, gl_tran_id, sweep_channel, narration, uploaded_by.
- payment_verification_systems (2024_05_14_104941_...) — system side: payment_verification_id, debtor_id,status, verified_by, verified_date, approved_by, approved_date, amount, document_no, reference.
Receipts / debtor transactions
- wa_debtor_trans (base 2023_09_08_134414_..., many later ALTERs) — the money-in record. State columns: verification_status (pending/verified/approved/manual upload, added 2024_06_05_114311_...), bank_statement_id (matched bank line, added 2024_06_08_085121_...), posting_date (GL posted, added 2024_10_20_134340_...), is_settled, reconciled (2024_04_13_130858_...), manual_upload_status, manual_upload_approved_by, verification_record_id, channel, bank_ref, wa_sales_invoice_id, wa_route_customer_id, wa_customer_id, document_no (RCT%, RCOP%, VAT%, BDT%).
- debtors_record_change_requests — debtor transfer requests: wa_debtor_trans_id, from_wa_customer_id, to_wa_customer_id, requested_by, approved_by,status(Pending/Approved/Rejected).
- debtor_trans_edit_requests — pending debtor edits (gated by EDIT_DEBTORS_TRANSACTION_REQUIRES_APPROVAL).
Exceptions
- suspended_transactions (2024_04_16_105156_...) — wa_customer_id, edited_wa_customer_id, suspended_by, resolved_by, document_no, reference, edited_reference, amount, edited_amount, trans_date, route, reason,status(defaultsuspended→expunged/restored),channel, branch, verification_record_id, manual_upload_status.
-unknown_bankings(2025_06_09_154602_...) —payment_verification_bank_id, user_id, **status** (default pending → claimed/resolved), requested_by/at, approved_by/at, gl_account_code.
-short_bankings_comments(2024_11_27_121744_...) —created_by, comment, amount (>0 short, <0 excess), **status** (Pending/Resolved), sales_date, branch_id, type, resolved_by/at, resolve_remarks, bank_statement_id, attachment, parent_comment_id. Linking tables:resolved_short_bank_statements,resolved_short_excess_links`.
Banking approval control
- banking_approvals (2024_09_28_094551_... + ALTERs) — one row per branch per day per sales type. Tracks the stage lifecycle. Confirmed columns include sales_date, payment_date, closed, closed_by; the controllers also read/write sales_type (1=POS, 2=route), verified/verified_by/verified_at, approved/approved_by/approved_at, closed_at, and a stage field (see Open Questions — these live in the later ALTER migrations, confirmed via controller usage rather than a single migration read).
CDM / cash banking
- banked_drop_transactions (2024_10_02_143114_...) — CDM deposits: cash_drop_transaction_id, bank_reference, cash_drop_reference, amount, banked_at,verified.
- banked_cash_transactions (2024_10_02_143231_...) — chief-cashier cash banking.
- cash_banking_reference_requests (2025_01_15_110834_...).
- chief_cashier_declarations, cash_drop_transactions — upstream POS cash sources (owned by POS chapter; read here).
Bank statement plumbing & logs
- bank_statement_consumers (2025_09_16_143218_...) — polymorphic junction linking a statement line to whatever consumed it (statement_id, consumer_id, consumer_type, amount, narration, user_id). Consumer types seen: WaGlTran, WaDebtorTran, WaPosCashSalesPayments, RegisterCheque, PaymentVerificationSystem, ShortBankingComment, BankedDropTransaction, CashBankingReferenceRequest, WithholdingPaymentVoucher, PaymentVoucherCheque.
- bank_statement_bank_errors (2024_09_07_163127_...) — flagged statement errors: payment_verification_bank_id, reason,status, created_by, restored_by, restored_date.
- bank_statement_edit_requests (2025_02_04_114713_...), bank_statement_mispost_histories (2024_09_05_134314_...).
- bank_opening_balances (2024_10_18_093431_...) — per-account opening balances for GL recon.
GL bridge
- gl_posting_logs (2024_06_27_114939_...) — audit of postings: created_by, transaction_no, document_no, amount, wa_banktrans_id, wa_debtor_trans_id, payment_verification_id.
- wa_gl_trans, wa_banktran — the GL/bank ledger tables written on approval (owned by Finance & Accounting; this module inserts money-in entries).
⚠️ Naming caution:
wallet_trans/WalletTranandwallet_matrix/WalletMatrixare NOT the reconciliation ledger.wallet_trans(app/WalletTran.php, migration2024_03_20_153827_...) is an employee-wallet table (employee_id, transaction_type= withdrawal/deposit/incentive,route_id, shift_id), andwallet_matrixholds salesman/driver commission-rate parameters. They belong to salesman incentives, not banking reconciliation. The real money-in ledger is thepayment_verification_*+wa_debtor_transfamily above.
5. Interactions with other modules (esp. GL posting handoff)¶
- Order Taking / POS / Cash Sales (upstream): receipts (
wa_debtor_trans), POS cash sales (wa_pos_cash_sales*), cash drops and chief-cashier declarations are created there; this module consumes and reconciles them. - Customer Management: debtor balances, unmatched-debtor matching to route customers/invoices, and debtor transfers reference
wa_customers/wa_route_customers. Bad debts (BDT-%documents) are explicitly excluded from route-banking GL approval (BankingApprovalController@approveBanking :1006). - General Ledger / Finance & Accounting (the handoff): this is the boundary this chapter owns up to. Money-in posts to the GL at four points:
- Route approval (
approveBanking): credit Debtors Control, debit Bank (or Fraud) →wa_gl_trans+wa_banktran. - POS approval (
approveAndCloseBanking): credit Cash Control (54008-000/branch), debit Bank (or Fraud). - Reconciliation approval (
PaymentReconciliationController@approval_store): queuesApproveReconciliationPayments, writesgl_posting_logs. - Sweeps / Unknowns / CDM (
postSweepsToGL,postUnknowns,allocateCdmDepositviaGlPostingService).
Downstream, the Finance book's GL reconciliation (App\Http\Controllers\Admin\GlReconciliationController, routes/modules/general_ledger.php:19-52, incl. ManualGLPostingController and gl-utility for pending-GL sales-payments/bank-statements/resolved-short-banking) closes out the chart-of-accounts side. The banking-side "GL Reconciliation" screen in this chapter (App\Http\Controllers\GlReconciliationController, gl-recon.overview) is a different, lighter tool that only reconciles bank-statement totals vs wa_banktrans per channel — don't confuse the two.
- SMS / Alerts: unknown-banking claim/resolve steps send SMS via SmsService (SmsScenarios::UNKNOWN_BANKING_CLAIM_REQUEST*).
- Number series: posting steps mint document numbers via NumberSeriesGeneratorService.
6. Alternatives & variants¶
Per-channel differences¶
Channels: M-Pesa, cheque, Eazzy, Vooma, KCB, Coop, CDM (plus Equity "EB").
- Bank channels split into "channel" vs "main" accounts. Equity, KCB, and Coop each have a route/POS channel account (
account_type=1, use_in_pos=0) and a "main" account (account_type=2) — hence separate gettersgetEazzy/getEbMain,getVooma/getKcbMain,getCoop/getCoopMain. - Payment-method IDs on restore (
SuspendedTransactionController@restore :351): Eazzy=7, Equity=10, Vooma=8, KCB=9, and M-Pesa/Cheque/others=3. - Channel matching is optional in auto-verification, toggled by
MATCH_CHANNEL_ON_PAYMENT_VERIFICATION. When off, matches key only on reference + amount + date. - CDM is a distinct flow: bank deposits at a Cash Deposit Machine are matched to physical cash drops (
banked_drop_transactions↔cash_drop_transactions) rather than to a digital reference, and post credit-Cash/debit-Bank viaGlPostingService. - Cheque payments interact with cheque management (register/deposit/bounce), outside this chapter; here they appear as a channel and as
getChequesin POS banking.
Manual vs automated¶
- Automated:
verification_process/runVerificationauto-match on reference/amount/date. - Manual:
ManualUploadControllerallocation + the Approve Uploads queue for anything that couldn't auto-match;BankingUtilityControllerfor corrective re-assignment (see below). - Statement ingestion is upload-based (Excel/CSV via
verification_uploadandBankStatementUploadController), not (from what was traced) a live bank API feed — see Open Questions.
Banking Utility (corrective / admin)¶
BankingUtilityController is the "fix it" toolkit (permission banking-utility): re-date/re-assign CDM records (updateCdmRecord), cash-banking records (updateCbRecord), and manual allocations (updateManualAllocationRecord); delete CDM / manual-allocation records; resolve duplicate POS sales (updateDuplicateSaleRecord, which re-numbers and re-books via PerformPostSaleActions); edit/delete bank-statement entries and add statement opening balances.
Flavor / tenant differences¶
Behaviour is toggled by Setting/company-preference flags, so it varies per client ("flavor"): MATCH_CHANNEL_ON_PAYMENT_VERIFICATION, SEPARATE_GL_ACCOUNTS_PER_BRANCH, BLOCK_COUNTER_SALES_WITH_EXCESS_UNBANKED_CASH, EDIT_DEBTORS_TRANSACTION_REQUIRES_APPROVAL, REQUIRE_APPROVAL_FOR_ROUTE_CUSTOMER_DEACTIVATION, unknown-bankings GL account preferences (unknown_bankings_gl_account / _2). Hard-coded GL codes seen: cash control 54008-000, fraud 55001-007.
Legacy¶
- A commented-out "Manual Uploads" menu (
sales_and_receivables.blade.php:803-808) pointing atmaintain-customers.real_recon.indexsuggests a superseded reconciliation entry point. routes/modules/sales_and_receivables.php.bakexists — an older copy of the routes.- A separate
BankReconciliationController(routesweb.php:5674-5677,bank-reconciliation.*) appears older/parallel to thepayment-reconciliation.*flow.
7. Open questions to confirm¶
banking_approvalsexact schema. The stage lifecycle (verified/approved/closed+stage1–4,sales_type1=POS/2=route) is proven by controller reads/writes (BankingApprovalController/PosBankingController), but onlysales_date, payment_date, closed, closed_bywere confirmed in a single migration read; the rest are spread across ALTER migrations (2024_11_28_233225,2025_06_18_161524,2025_06_30_214256). Confirm the full column list and whetherstagenumbers differ between route (max 3) and POS (max 4).is_settledvsverification_statusvsreconciled.wa_debtor_transhas three overlapping "state" flags. Confirm which is authoritative for "matched" at each step (the state machine here treatsverification_status+bank_statement_idas primary;is_settled/reconciledmay be legacy or invoice-allocation flags).- Suspended status values. Migration default is
suspended;expunge/restoresetexpunged/restored, butBankingApprovalController@suspendUnverifiedwrites statusexpungeddirectly at creation. Confirm the complete set of valid statuses and whether "resolved" is also used. - Unknown-banking
claimedstatus. The create migration comments onlypending/approved, but the controller clearly usespending → claimed → resolved. Confirm the DB actually storesclaimed/resolved(vsapproved) and that reject truly reverts topending. - Automated bank feeds? All statement ingestion traced is manual upload (Excel/CSV). Confirm whether any client uses an automated/API bank feed for M-Pesa/Equity/KCB, or whether
ProcessUploadedBankStatements/SyncUnknownBankingJobimplies a scheduled sync. - Two GL-reconciliation controllers. Confirm the intended division of labour between banking-side
App\Http\Controllers\GlReconciliationController(this chapter) and Finance-sideApp\Http\Controllers\Admin\GlReconciliationController(Finance book), and whether both are live per flavor. - Route-Approval sidebar permissions.
sales_and_receivables.blade.php:464/471/477gate Route-Approval links onreconciliation___*permissions — confirm this is intentional or a copy-paste artifact. ManualUploadControllerpermission on write.manual_upload_transactionhad no explicitcan()check in the trace; confirm whether authorization is enforced at the route/middleware level.wa_debtor_tran_reconstable — its role (a flagged/batch reconciliation-tracking side table) was inferred from schema only; confirm where it's written and read.
8. Source references¶
Nav / IA
- resources/views/admin/includes/sidebar_includes/sales_and_receivables.blade.php:709-898 (Banking + Reconciliation menus & permissions)
Routes
- routes/modules/sales_and_receivables.php:147-517 (banking prefix: route/pos/gl-recon/utility/unknown-bankings; reconciliation overview; transaction-history; view-invoice-payments; debtor transfers; unmatched debtors)
- routes/web.php:5679-5773 (payment-reconciliation verification/matching/approval; bank-post-log; debtor-trans; bank-statements; manual-upload; suspended-transactions)
- routes/modules/general_ledger.php:19-52,177-180 (Finance-side GL reconciliation & bank-recon utility — boundary)
Controllers
- app/Http/Controllers/BankingApprovalController.php (Route Sales Banking; approveBanking:989, suspendUnverified:853, postSweepsToGL:2751, postUnknowns:5746, channel getters:1102-2316)
- app/Http/Controllers/PosBankingController.php (Cash Sales Banking; runVerification:1850, approveAndCloseBanking:6188, closeBanking:6337, CDM:2034-2421, short banking:4557-7944)
- app/Http/Controllers/GlReconciliationController.php (banking-side GL recon; getRecords:83, opening balances:38-81)
- app/Http/Controllers/Admin/BankingUtilityController.php (corrective utility)
- app/Http/Controllers/Admin/UnknownBankingsController.php (claim/approve; claim:532, approveClaim:579, rejectClaim:630, bulk:688-906)
- app/Http/Controllers/Admin/Finance/PaymentReconciliationController.php (verification_process:42, verification_upload:531, matchingRecordsView:949, approval:1347, bank_post_logs:1697)
- app/Http/Controllers/Admin/SuspendedTransactionController.php (store:120, expunge:268, restore:305)
- app/Http/Controllers/Admin/DebtorRecordChangeRequestController.php (debtor transfers)
- app/Http/Controllers/Admin/Finance/DebtorTransController.php (debtor-trans listing & edit requests)
- app/Http/Controllers/Admin/Finance/UnmatchedDebtorsController.php (unmatched:100, match:165)
- app/Http/Controllers/Admin/ManualUploadController.php (manual_upload_transaction:46, manual_upload_list:671)
- app/Http/Controllers/Admin/BankStatementUploadController.php (statement upload, channel statements, bank_error_logs)
- app/Http/Controllers/Admin/TransactionHistoryController.php, app/Http/Controllers/Admin/ViewInvoicePaymentsController.php
Models / enums
- app/Enums/Status/PaymentVerification.php (status enum)
- app/Models/PaymentVerification.php, PaymentVerificationBank.php, PaymentVerificationSystem.php
- app/Models/SuspendedTransaction.php, app/Models/UnknownBanking.php, app/Models/ShortBankingComment.php, app/Models/BankStatementConsumer.php, app/Models/BankStatementBankError.php
- app/Model/WaDebtorTran.php
- (Not this module: app/WalletTran.php, app/WalletMatrix.php — employee wallet/commission, see §4 caution)
Migrations (database/migrations/)
- 2024_05_13_121816_create_payment_verifications_table.php, 2024_05_14_104950_create_payment_verification_banks_table.php, 2024_05_14_104941_create_payment_verification_systems_table.php
- 2023_09_08_134414_create_wa_debtor_trans_table.php + ALTERs (2024_04_13_130858, 2024_06_05_114311, 2024_06_08_085121, 2024_10_20_134340, …)
- 2024_04_16_105156_create_suspended_transactions_table.php (+ 2024_06_14_123444, 2024_08_14_091629)
- 2025_06_09_154602_create_unknown_bankings_table.php (+ 2026_05_04_*, 2026_05_04_130000 gl_account_code)
- 2024_09_28_094551_create_banking_approvals_table.php (+ ALTERs 2024_11_28, 2025_06_18, 2025_06_30, index 2026_01_17)
- 2024_11_27_121744_create_short_bankings_comments_table.php (+ resolve columns 2025_08_01, 2025_08_06, 2025_08_07); 2025_08_08_164816_create_resolved_short_bank_statements_table.php
- 2024_10_02_143114_create_banked_drop_transactions_table.php, 2024_10_02_143231_create_banked_cash_transactions_table.php, 2025_01_15_110834_create_cash_banking_reference_requests_table.php
- 2025_09_16_143218_create_bank_statement_consumers_table.php, 2024_09_07_163127_create_bank_statement_bank_errors_table.php, 2025_02_04_114713_create_bank_statement_edit_requests_table.php, 2024_09_05_134314_create_bank_statement_mispost_histories_table.php
- 2024_10_18_093431_create_bank_opening_balances_table.php, 2024_06_27_114939_create_gl_posting_logs_table.php
Views
- resources/views/banking_approval/ (route_overview, pos_daily_overview, route_banking_show, short_bankings_comments, utility, PDFs)
- resources/views/reconciliation/payments/overview.blade.php (recon dashboard: Debtors Balance, Sales vs Receipts, Reconciliation Issues, Reconciliation Resolutions)
- resources/views/gl_recon/overview.blade.php (opening balance / system debits-credits / variance)