Skip to content

Book 4, Chapter 17 — Petty Cash & Expenses

1. Purpose

Petty Cash & Expenses is the module a Bizwiz distributor uses to pay for small, operational things that fall outside the normal supplier-invoice / accounts-payable flow — fuel and repairs for a delivery truck, materials to fix a building, casual labour on a project, a driver's travel allowance for a delivery run, or an ad-hoc "send this person some money" transfer. In Kenya almost all of these payouts settle over M-Pesa B2C (business-to-customer mobile-money transfers), so the module is really two things bolted together:

  1. An expense-authorisation workflow — someone requests money for a legitimate business reason, one or more approvers sign off, a voucher is cut, and only then does money disburse.
  2. A mobile-money disbursement + reconciliation engine — the actual payout is an M-Pesa B2C send, whose float has to be topped up, whose transaction charges (tariffs) have to be tracked, and whose statement has to be reconciled against what the ERP thinks it paid.

The whole thing is built around one repeated shape: requisition → approval(s) → voucher → disbursement, and that same engine is reused for four different "expense centres" (Vehicle, Buildings, General, Project) just by passing a different PettyCashSources value. On top of that sit a few specialised flavours: Send Money (a bare M-Pesa transfer with no requisition), Budgeted Expenses (travel allowances auto-computed from delivery / order-taking / GRN routes), and Casual Disbursements (paying casual labourers per pay-period). Everything that pays out also posts to the General Ledger (wa_gl_trans, Book 4 Ch15) and files its supporting documents.


Technical framing. The nav boundary is resources/views/admin/includes/sidebar_includes/pettycash.blade.php, top-level gated by petty_cash___view (pettycash.blade.php:15). Routes live in a single file, routes/modules/petty_cash.php (455 lines), all under prefix admin/petty-cash, name prefix petty-cash., middleware ['AdminLoggedIn','ip-blocker'] (petty_cash.php:45-47).

The App\Enums\PettyCashSources enum (app/Enums/PettyCashSources.php) is the spine — a string-backed enum whose values are human labels (not the // 1..11 comment codes):

case VehicleCentre   = 'Vehicle Centre';    // 1
case BuildingCentre  = 'Building Centre';   // 2
case ProjectCenter   = 'Project Center';    // 3
case BudgetedExpenses= 'Budgeted Expenses'; // 4
case SalesmanIncentives = 'Salesman Incentives'; // 7
case Miscellaneous   = 'Miscellaneous';     // 8
case GeneralExpenses = 'General Expenses';  // 9
case CasualPay       = 'Casual Pay';        // 10
case ProjectCasualPay= 'Project Casual Pay';// 11

Note the enum has more sources than the sidebar exposes (SalesmanIncentives, CasualPay, ProjectCasualPay), and the string value 'Building Centre' is singular even though the nav group is "Buildings Centre" — a naming inconsistency worth flagging.

2. Users & roles

Access is via the standard Bizwiz permission map: $my_permissions[...], checked in the sidebar and in can() (app/helpers.php:4182). The super-role bypass is $logged_user_info->role_id == config('app.allowed_role'), checked on every nav line. Typical roles who touch this module: branch/petty-cash clerks (initiate requisitions, Send Money), approvers/managers (approve-expense, approve-documents, approve-lpo, voucher approval), finance/treasury (disburse, reconciliation, tariffs, top-ups), and document controllers (filing).

Permission strings gated in the sidebar (pettycash.blade.php), grouped by centre. Every string uses the <area>___<action> convention (triple underscore):

Area View perm Sub-action perms seen in nav
Module root petty_cash___view —
Send Money send_money___view ___initiate, ___view-pending, ___view-disbursed, ___view-failed
Project Centre project_centre___view ___maintain-suppliers, ___maintain-items; requisitions: project_center_requisitions___view, ___view-disbursements
Vehicle Centre vehicle_centre___view ___view-vehicle-list, ___maintain-expense-types, ___maintain-suppliers, ___maintain-items; driver_expense_requests___view; requisitions: vehicle_center_requisitions___view, ___view-disbursements
Buildings Centre buildings_centre___view ___view-building-list, ___maintain-suppliers, ___maintain-items; requisitions: building_center_requisitions___view, ___view-disbursements
General Expenses general_expenses___view requisitions: general_expense_requisitions___view, ___view-disbursements
Budgeted Expenses budgeted_expenses___view ___view-pending, ___view-disbursed; settings: budgeted_expense_settings___view, scheduled_expenses___view, travel_grn_routes___view
Casual Disbursements normal_casual_disbursements___view ___view-pending, ___view-disbursed
Documents petty_cash_documents___view —
Reconciliation petty_cash_reconciliation___view ___manage-tariffs, ___manage-top-ups, ___view_disbursements, ___view-statements
Reports petty_cash_reports___view —

Flag two inconsistencies in the permission naming, verbatim: the reconciliation disbursements perm is petty_cash_reconciliation___view_disbursements (single underscore before disbursements, pettycash.blade.php:560) whereas statements uses a hyphen ___view-statements (:568) — mixed conventions. Also note the centres use singular *_center_requisitions___view (American spelling, e.g. vehicle_center_requisitions, building_center_requisitions, general_expense_requisitions) while the top-level nav perms use British *_centre___view.

(The authoritative definition/seed of these strings is a §7 open item pending the permissions-seeder search; the nav is the observed boundary.)

3. Processes

Controller map (route → controller)

Everything below is derived directly from routes/modules/petty_cash.php. The single most important fact: the shared requisition/voucher/disbursement engine for Vehicle / Buildings / General is App\Http\Controllers\PettyCashPurchaseOrderController (route group petty-cash.requisitions.*, petty_cash.php:309-357), not a class literally named "Requisition". Project Centre has its own parallel engine, ProjectCentreRequisitionController (petty_cash.php:82-105). This is a key subtlety: "the shared engine" is shared across three of the four centres; Project is a fork.

Nav group Route prefix Controller Key lines
Send Money (B2C) petty-cash.miscellaneous.* MiscellaneousPettyCashRequestController 287-307
Shared requisitions (Vehicle/Building/General) petty-cash.requisitions.* PettyCashPurchaseOrderController 309-357
Project Centre requisitions petty-cash.project-centre.requisitions.* ProjectCentreRequisitionController / ProjectCentrePurchaseOrderController 82-105
Vehicle Centre requisitions (own group) petty-cash.vehicle-centre.requisitions.* VehicleMaintenanceRequisitionController 168-179
Budgeted Expenses petty-cash.budgeted-expenses.* BudgetedExpenseController 373-385
Scheduled Expenses (settings) petty-cash.scheduled-expenses.* ScheduledExpenseController 364-371
Travel GRN Routes petty-cash.travel-grn-routes.* TravelGrnRouteController 387-399
Inter-branch GRN rates petty-cash.inter-branch-travel-grn-routes.* InterBranchTravelGrnRouteRateController 413-416
Casual Disbursements (normal) petty-cash.normal-casual-disbursements.* CasualPayDisbursementController 401-410
Project Casual Disbursements petty-cash.project-centre.casual-disbursements.* ProjectCentreRequisitionController 128-132
Documents petty-cash.documents.* FilingRequestController 281-285
Reconciliation — statements petty-cash.reconciliation.statements.* PettyCashStatementController 233-238
Reconciliation — records petty-cash.reconciliation.records.* PettyCashReconciliationRecordController 240-247
Reconciliation — disbursements petty-cash.reconciliation.disbursements.* DisbursementController 249-252
Tariff periods / charges petty-cash.reconciliation.tariffs.* MpesaB2cTariffChargePeriodController / MpesaB2cTariffChargeController 254-271
B2C Top-ups petty-cash.reconciliation.top-ups.* MpesaB2cTopUpController 273-278
M-Pesa balance checks petty-cash.check-balance etc. MpesaBalanceTrackerController 359-362
Reports petty-cash.reports.* PettyCashReportController + summary/statement report controllers 418-425
Dashboards petty-cash.dashboard.* / projects-dashboard.* PettyCashDashboardController / ProjectsDashboardController 427-453

Note the disbursement plumbing (MpesaB2c*, MpesaBalanceTracker, DisbursementController) is not petty-cash-namespaced — these controllers live at the top of app/Http/Controllers/ and are shared infrastructure. M-Pesa B2C is the payout rail for the whole module.

3.1 Shared requisition → voucher → disbursement lifecycle

This is the core process, reused for Vehicle / Buildings / General Expenses, keyed by ?module=<PettyCashSources value>. All handlers are on PettyCashPurchaseOrderController.

The routes reveal the full state machine as a sequence of POST actions: create → store → (approve-expense | reject-expense) → attach-documents → approve-documents (or skip-documents) → approve-tax-invoice → save-voucher → submit → disburse → check-status, with failure branches failed → expedite | resend | update-payee-info and terminal discard-expense (expunge). The list views are index (pending), vouchers (pending vouchers), rejected, paid/paid.data (disbursed), failed.

flowchart TD
    A[Create requisition\ncreate/store] --> B{approve-expense?}
    B -- reject-expense --> R[Rejected]
    B -- discard-expense --> X[Expunged/Discarded]
    B -- approved --> C[Attach documents\nattach-documents]
    C --> D{approve-documents\nor skip-documents}
    D --> E[approve-tax-invoice]
    E --> F[save-voucher\n= Pending Voucher]
    F --> G[submit]
    G --> H[disburse\nM-Pesa B2C send]
    H --> I{check-status}
    I -- success --> P[Paid / Disbursed\n+ GL post]
    I -- failure --> FL[Failed]
    FL -- resend / expedite --> H
    FL -- update-payee-info --> H

The module query param selects which centre's master data (suppliers, items, expense accounts) and which GL expense account applies; the engine, tables, and lifecycle are identical across the three shared centres. (State-value details — the exact status strings/constants — are captured in §4 and §7 pending confirmation from the requisition table + controller.)

3.2 Send Money (Miscellaneous B2C payout) — no requisition

Send Money is the "just pay this person" path with no requisition/voucher ceremony — it is a direct M-Pesa B2C transfer, singly (create/initiate) or in bulk (bulk-create/bulk-initiate, template upload), handled by MiscellaneousPettyCashRequestController (petty_cash.php:287-307).

flowchart LR
    A[Send to One / Send to Many\ncreate / bulk-create] --> B[initiate / bulk-initiate]
    B --> C[Pending Requests\npending.approve]
    C --> D[process / bulk-process\n= M-Pesa B2C send]
    D --> E{result}
    E -- ok --> S[Successful Disbursements]
    E -- fail --> F[Failed Disbursements]
    F -- resend --> D
    F -- discard --> X[Discarded]

Sending is itself permission-gated per-user: allow/revoke routes (petty_cash.php:305-306) call UserController::allowSendingMiscellaneousPettyCash — i.e. an admin explicitly grants a user the right to send miscellaneous petty cash. There is a grns-by-branch helper (:298) suggesting Send Money can be tied to GRN context.

3.3 Budgeted travel expense auto-generated from a route

Budgeted Expenses is the auto-computed travel-allowance engine. Instead of a person filing a requisition, the system generates a pending expense whenever an operational route (an order-taking round, a delivery run, or a GRN inter-branch trip) occurs, priced from Travel GRN Routes + Inter-Branch GRN Rates + Route Parking Fees, on a schedule defined by Scheduled Expenses.

flowchart TD
    RT[Travel GRN Route / Inter-branch rate\n+ Route Parking Fee] --> SE[Scheduled Expense config]
    OP[Operational event:\norder-taking / delivery / GRN route run] --> GEN[Auto-generate budgeted expense]
    SE --> GEN
    GEN --> PEND["Pending\nbudgeted-expenses.pending?module=order-taking|delivery|grn"]
    PEND --> AP[approve]
    AP --> DIS[disburse -> M-Pesa B2C]
    DIS --> CK{check-status}
    CK -- ok --> OK[Successful Disbursements\nbudgeted-expenses.disbursed]
    CK -- fail --> FL[Failed\nexpedite / resend / reverse]

The pending view is parameterised by module=order-taking|delivery|grn (pettycash.blade.php:396-412). Actions: approve (may apply an offset_amount penalty), disburse (queues BulkDisburseExpenses), discard, reverse, check-status, and failed-handling expedite/resend (rate-limited throttle:1,3, :383).

Generation trigger (confirmed): app/Services/BudgetedExpenseTravelGrnVehicleRevenueRecorder.php creates a budgeted_expenses row when a GRN is received with its linked delivery complete — syncSupplierTravelGrnExpenseFromGrnReceive() (~L190-254, parent_type supplier_travel_grn, priced from travel_grn_routes.mileage_amount) and syncInterBranchTravelGrnExpenseFromGrnReceive() (~L259-314, parent_type inter_branch_travel_grn, priced from inter_branch_travel_grn_route_rates.rate by origin/destination branch + vehicle type). It flags potential duplicate demands (grn_demand_is_potential_double) when the same driver+route recurs on one day. The order-taking / delivery / scheduled paths and scheduled_expenses-driven generation exist as code but appear partly commented out in the recorder (a §7 confirm).

3.4 Reconciliation (M-Pesa float → statement → close)

Because every payout is real M-Pesa money, the module reconciles ERP disbursements against the mobile-money portal:

flowchart TD
    TU[B2C Top-Up\nfloat added to paybill] --> BAL[MpesaBalanceTracker]
    DISB[All disbursements across centres] --> STMT[Upload / import statement\nstatements.process]
    STMT --> REC[Reconciliation record\nrecords.pending]
    REC --> V[run-verification -> complete-verification]
    V --> CL[close -> Approved & Closed]
    TAR[Tariff Periods + Charge Matrix] --> STMT
  • Top-Ups (MpesaB2cTopUpController) record float added to the B2C working account.
  • Tariff Charge Periods + Charge Matrix (MpesaB2cTariffChargePeriodController / MpesaB2cTariffChargeController) hold the M-Pesa transaction-fee schedule so the ERP can predict/verify charges per transaction band.
  • Statements (PettyCashStatementController) import the actual M-Pesa statement.
  • Records (PettyCashReconciliationRecordController) run verification (runVerification → completeVerification) and close a period into "Approved & Closed".
  • All Disbursements (DisbursementController::index, updateFromPortal) is the cross-centre disbursement ledger, refreshable from the portal.

3.5 Casual disbursements & document filing

  • Casual Disbursements (CasualPayDisbursementController, :401-410) pay casual labourers per pay period: Pending Pay Periods → disbursePayPeriod → M-Pesa send → checkStatus, with resendFailed. Project casuals are a parallel path on ProjectCentreRequisitionController (:122-132).
  • Document Management (FilingRequestController, :281-285): disbursed requisitions raise a filing request ("Ready For Filing") for their receipts/vouchers; fileDocument moves them to "Filed Documents". This closes the audit loop after payout.

4. Tables touched & key data

The requisition spine — petty_cash_purchase_orders

Despite the "purchase order" name, this is the requisition table for the shared engine (Vehicle/Building/General/Project) — migration 2025_03_19_121540_create_petty_cash_purchase_orders_table.php:13-44. Key columns:

Column Meaning
requisition_number PCR-series document number (via NumberSeriesService)
lpo_number SOR-series number, populated for Vehicle/Building supplier LPOs
parent_id / parent_type polymorphic owner — the vehicle / building / project (parent_type is a PettyCashIntegerSources code 1/2/3/…)
stage the state-machine column (see enum below)
resolved, rejected, rejection_reason branch flags (rejected/rejection_reason added in 2025_04_12_201641:14-15)
requested_by, rejected_by, disbursed_by, voucher_created_by, voucher_approved_by, tax_invoice_approved_by approval/actor audit columns
voucher_approved_by_chairman boolean — a "chairman" approval gate on vouchers
tax_invoice_pending_approval, tax_invoice_approved_at tax-invoice sub-approval (2026_02_27_140001)
preferred_payment_method PettyCashPaymentMethods (B2C direct, B2B paybill, B2B till, payment voucher = 4)
recipient_phone_number, originator_conversation_id M-Pesa Daraja payout target + conversation ID for status tracking
archived soft-hide

Line items: petty_cash_purchase_order_items (2025_03_19_121550:13-34) — petty_cash_purchase_order_id, polymorphic parent_id/parent_type (the item master), quantity, cost, tax_manager_id, tax_value, discount, narration, expense_category, vehicle_center_category_id.

The state machine — PettyCashRequestStage

app/Enums/PettyCashRequestStage.php:7-16 — the stage values:

requested → (approved, lpo = obsolete) → document_verification → document_upload
          → review → disbursement → disbursed | payment_voucher → paid

So the "initial approval / final approval" language from the general-ledger nav maps here to approveExpense() (→ approved), approveLpo() (→ document_verification), and approveDocuments() (→ disbursement). Rejection sets rejected=true; discard/expunge removes it.

The voucher / payout engine — SHARED with Book 3 Ch13 AP (CONFIRMED)

Petty cash vouchers settle through the same polymorphic payment-voucher engine as Accounts Payable:

  • payment_vouchers (2024_02_27_031640:14-24) — wa_supplier_id, wa_bank_account_id, wa_payment_method_id, status, amount, prepared_by, number, approved_by, approved_at.
  • payment_voucher_items (2024_02_27_031806, altered 2024_06_23_171511:12-13) — the polymorphic link: payable_type (default 'invoice', but also petty-cash values) + payable_id (renamed from wa_supp_trans_id). This confirms the Ch13 seam: petty cash requisitions paid by "payment voucher" (preferred_payment_method = 4) ride the AP payment-voucher rail, and a wa_supp_trans supplier-bill record is created alongside.

The chapter brief flagged payment_voucher_items.payable_type including petty_cash_lpo — the migration proves the polymorphic column exists and defaults to 'invoice'; the exact petty-cash payable_type string value used at runtime is a §7 item (the sub-agent saw it referenced but the literal string wasn't pinned).

Legacy tables (still present)

wa_petty_cash_requests (2024_06_08_090135:14-35) + wa_petty_cash_request_items (2024_06_08_092433) are an older petty-cash request model with explicit initial_approval/initial_approver/initial_approval_date and final_approval/final_approver/final_approval_date columns — the two-step approval the GL nav referenced. The modern engine (petty_cash_purchase_orders.stage) supersedes this, but both coexist.

Budgeted-expense tables

  • budgeted_expenses (2025_03_18_153455) — petty_cash_type_id, recipient_id, recipient_phone_number, amount, offset_amount (penalty deducted), parent_id/parent_type (source: supplier_travel_grn / inter_branch_travel_grn / travel_order_taking / travel_delivery / scheduled_expense), branch_id, vehicle_id, originator_conversation_id, plus state flags approved/disbursed/paid/failed, failure_reason, grn_demand_is_potential_double, document_number, document_link, scheduled.
  • travel_grn_routes (2025_04_04_062251) — route_name (unique), slug, distance, revenue_estimate, fuel_estimate, mileage_amount (the payout), is_inter_branch, fueling_branch_id, revenue_rate_per_km, calculate_revenue_per_rate.
  • inter_branch_travel_grn_route_rates (2025_04_21_123643) — origin_branch_id, destination_branch_id, vehicle_responsibility (carton_truck / prime_mover), rate, distance_estimate, fuel_estimate, fueling_branch_id.
  • scheduled_expenses (2025_03_18_131120) — petty_cash_type_id, recipient_id, recipient_phone_number, amount, branch_id, frequency_difference_in_days, start_date, time, notes, archived.

Route Parking Fees is listed in the chapter scope but there is no route-parking-fees route group and no obvious table — flag as likely dropped/renamed (see §7).

Disbursement & reconciliation tables

  • disbursements — document_no (PCD-*), receipt_no (M-Pesa receipt, the reconciliation match key), phone_number, amount, originator_conversation_id, polymorphic parent_id/parent_type (e.g. a budgeted_expenses.id), transaction_time, status.
  • petty_cash_statements (2025_02_26_150708) — imported M-Pesa lines: paybill_number, receipt_number, completion_time, credit, debit, status, payee, reason, narrative, body (raw JSON), disbursement_id (linked once verified), transaction_type (transaction | charge).
  • petty_cash_reconciliation_records — per-day record with verified, closed, comments; verified by matching disbursements.receipt_no ↔ petty_cash_statements.receipt_number.
  • mpesa_b2c_top_ups (2025_04_17_103823) — top_up_date, amount, reference, narration, recorded_by, top_up_by (float additions; appear as statement debits).
  • mpesa_b2c_tariff_charge_periods (2025_04_15_134136) — start_date, end_date, active_to_date.
  • mpesa_b2c_tariff_charges (2025_04_15_144010) — period_id, lower_limit, upper_limit, charge — the banded M-Pesa fee matrix used to predict/verify charges.

GL posting (confirmed) — wa_gl_trans

Two services post to the GL spine (wa_gl_trans, Ch15):

PettyCashDisbursementGlPostingService — on B2C/B2B settlement (PCD-* series), it credits petty-cash control 54000-001 and debits an expense account by source: - Building/Project → 56003-001, Vehicle → 56002-012, Casual wages → 56002-033; - General Expenses → resolved per line item from WaPettyCashRequestType.wa_charts_of_account_id; - Miscellaneous/Budgeted → from the item's petty-cash type's chart-of-account. It mirrors the live posting in DarajaDisbursementController::processCallBack().

PettyCashLpoAccrualGlPostingService — for voucher-paid (accrual) requisitions (SOR-*): DR expense, DR VAT control (if >0.01), CR creditors control (VAT-inclusive), and creates/updates a wa_supp_trans supplier bill. Called from PettyCashPurchaseOrderController::approveDocuments() via recordVoucherPaidLpoAccrual() and is idempotent (checks for existing GL before reposting).

(Exact account-code constants and file:line for these services are in §8; the sub-agent cited PettyCashDisbursementGlPostingService.php credit at line 31 and debit-by-source at lines 190-228, and the accrual service around lines 227-318.)

5. Interactions with other modules

  • General Ledger (Book 4 Ch15, wa_gl_trans). Every disbursement posts via PettyCashDisbursementGlPostingService (credit petty-cash control 54000-001, debit expense-by-source) or, for voucher-paid accruals, PettyCashLpoAccrualGlPostingService (DR expense / DR VAT / CR creditors + wa_supp_trans). The disbursement GL posting mirrors DarajaDisbursementController::processCallBack(), i.e. it fires on the M-Pesa settlement callback, not at click time.
  • Accounts Payable (Book 3 Ch13) — SHARED payment-voucher engine (CONFIRMED). When a requisition's preferred_payment_method = 4 (payment voucher), it settles through the same payment_vouchers / payment_voucher_items polymorphic tables that AP uses; payable_type/payable_id carry the petty-cash requisition. A wa_supp_trans supplier bill is raised, so petty-cash LPOs become creditor liabilities exactly like AP invoices. The chart-of-account resolution reuses SupplierChartOfAccountService.
  • M-Pesa / Daraja disbursement rail (shared infra). MpesaBalanceTracker, DisbursementController, MpesaB2c*, and the injected disbursement service (disburse(), disburseToPaybill(), disburseToTill()) are the payout mechanism for the whole module — B2C direct sends, B2B paybill, B2B till. Not petty-cash-namespaced; shared with other payout features.
  • Order-taking / Delivery / GRN (Books 1–3) → Budgeted Expenses. The auto-travel engine BudgetedExpenseTravelGrnVehicleRevenueRecorder hooks into GRN receive (WaReceivePurchaseOrder / ConfirmedReceiveOrderController) and inter-branch transfers (NWaInventoryLocationTransfer) to auto-create budgeted_expenses priced from travel_grn_routes.mileage_amount and inter_branch_travel_grn_route_rates.rate, gated on the linked delivery being complete. Order-taking / delivery travel-allowance paths exist but appear partly commented (see §6/§7).
  • Fleet (Book 5) — boundary. Vehicle Centre here is the expense/disbursement side (maintenance requisitions, supplier LPOs, driver expense requests, vehicle expense categories). Disbursing a vehicle requisition writes a VehicleCost / VehicleAssetAmortization record (PettyCashPurchaseOrderController::disburse() ~lines 3890-3936), which is the seam into the Book 5 operational vehicle/fuel/tyre side. Book 5 owns the vehicle master and operational usage; Ch17 owns paying for it.
  • HR / Users. Requisition recipients and casual-pay disbursements resolve against users / HR employees (hr-employees-by-branch, users-by-branch helpers). Send Money rights are granted per-user via UserController::allow/revokeSendingMiscellaneousPettyCash.
  • Task Center. Requisition review/disburse steps register tasks (keys like petty_cash_vehicle_requisition_review / _disburse) validated through TaskCenterService — the same workflow-task backbone used elsewhere in the ERP.

6. Alternatives & variants

The four+ "sources". The shared engine is parameterised by PettyCashSources, but the sidebar only exposes Vehicle / Building / General on the shared petty-cash.requisitions.* controller. Project Centre is a fork — its own ProjectCentreRequisitionController / ProjectCentrePurchaseOrderController, with extra concepts (project categories, casuals, pay-periods, processed invoices, budget-vs-actual dashboard) the others lack. Vehicle Centre also has a second, older requisition group (petty-cash.vehicle-centre.requisitions.* → VehicleMaintenanceRequisitionController) distinct from the shared one — a likely legacy path.

Manual vs. auto expenses. A requisition is manually filed and approved; a budgeted expense is auto-generated (from a GRN/route) then merely approved+disbursed. Send Money skips requisition entirely. Casual disbursements batch by pay-period. So there are effectively four creation styles feeding one M-Pesa payout rail.

Payment methods. PettyCashPaymentMethods — B2C direct send, B2B paybill, B2B till, and payment voucher (=4). Only the voucher path invokes the AP accrual/creditor posting; the M-Pesa paths post the direct disbursement GL. A "chairman" voucher-approval gate (voucher_approved_by_chairman) applies on the voucher path.

Legacy coexistence. wa_petty_cash_requests (with explicit initial_approval/final_approval columns) predates the petty_cash_purchase_orders.stage engine; both are still in the schema. The GL nav's petty-cash-requests-initial-approval / -final-approval states most likely reference the legacy table.

Tenant/feature-flag differences. Which centres a distributor sees is driven entirely by the granular *___view permissions per tenant, so a small distributor may only run Send Money + General Expenses while a large one runs all centres + budgeted travel. The enum carries SalesmanIncentives, CasualPay, ProjectCasualPay sources with no dedicated sidebar group, implying either programmatic-only use or per-tenant nav gating.

Edge cases seen in routes: failed-disbursement expedite/resend/update-payee-info; reverse-invoice and reverseUnpaidLpoAccrual (GL reversal on rejection); skip-documents (bypass filing); OTP + B2B-balance checks before payout; idempotent GL posting to avoid double-posting on callback retries.

7. Open questions to confirm

CODE-PROVEN (file:line)

  • Shared requisition engine = PettyCashPurchaseOrderController; Project is a fork = ProjectCentreRequisitionController (routes/modules/petty_cash.php:309-357 vs :82-105).
  • State machine lives in petty_cash_purchase_orders.stage, values from app/Enums/PettyCashRequestStage.php:7-16 (requested → document_verification → document_upload → review → disbursement → disbursed|payment_voucher → paid).
  • Polymorphic AP payment-voucher engine is shared: payment_voucher_items.payable_type/payable_id (database/migrations/2024_06_23_171511:12-13).
  • GL posts to wa_gl_trans: credit 54000-001, debit expense-by-source in PettyCashDisbursementGlPostingService (~L31, L190-228); accrual DR/DR/CR in PettyCashLpoAccrualGlPostingService.
  • Budgeted expenses auto-generated from GRN receive in BudgetedExpenseTravelGrnVehicleRevenueRecorder (~L190-314); tables budgeted_expenses, travel_grn_routes, inter_branch_travel_grn_route_rates, scheduled_expenses.
  • Reconciliation matches disbursements.receipt_no ↔ petty_cash_statements.receipt_number (PettyCashReconciliationRecordController::runVerification ~L256-295).
  • Naming inconsistencies (verbatim): route names updatee (petty_cash.php:90, 329); perm petty_cash_reconciliation___view_disbursements (underscore) vs ___view-statements (hyphen) (pettycash.blade.php:560, 568); enum value 'Building Centre' singular; *_center_requisitions (US) vs *_centre___view (UK).

INFERENCE / needs human validation

  • Exact petty-cash payable_type string written into payment_voucher_items (brief mentioned petty_cash_lpo) — the polymorphic column is proven but the runtime literal for petty cash was not pinned.
  • Route Parking Fees — in the chapter scope and referenced conceptually, but no route group and no clear table were found. Likely renamed, folded into route rates, or dropped. Confirm.
  • Order-taking / delivery / scheduled budgeted-expense generation appears partly commented in the recorder service — confirm whether these paths are live in production or only the GRN paths run.
  • Legacy wa_petty_cash_requests — confirm whether any tenant still writes to it, or whether it is purely historical; and confirm it is what the GL nav's -initial-approval/-final-approval states point to.
  • Permission seed source — the authoritative definition of the *___view strings (seeder/config) was gated to a separate agent still resolving; treat the sidebar as the observed boundary until confirmed.
  • Exact GL account codes beyond those cited (e.g. VAT/creditors/rounding come from WaCompanyPreference) are tenant-config, not hardcoded — verify per tenant.
  • SalesmanIncentives / CasualPay / ProjectCasualPay enum sources with no sidebar group — confirm where/if they surface in the UI.

8. Source references

Nav & enums - resources/views/admin/includes/sidebar_includes/pettycash.blade.php — nav boundary, perm gates (root :15, send-money :22-67, project :72-155, vehicle :157-247, buildings :249-323, general :325-374, budgeted :376-470, casual :472-496, documents :498-520, reconciliation :522-593, reports :595-601). - app/Enums/PettyCashSources.php — the source enum (9 cases). - app/Enums/PettyCashRequestStage.php:7-16 — requisition state machine. - app/helpers.php:4182 — can() permission check.

Routes — routes/modules/petty_cash.php - Group root :45-47; project-centre :48-133; vehicle-centre :135-197; buildings-centre :199-230; reconciliation :232-279; documents :281-285; miscellaneous/Send-Money :287-307; shared requisitions :309-357; M-Pesa balance :359-362; scheduled-expenses :364-371; budgeted-expenses :373-385; travel-grn-routes :387-399; normal-casual-disbursements :401-410; inter-branch-grn-rates :413-416; reports :418-425; dashboards :427-453.

Controllers (all under app/Http/Controllers/, Admin/ where noted) - PettyCashPurchaseOrderController.php (shared engine; disburse() ~3822-4093, approveDocuments() ~3551-3592, approveExpense() ~3425, approveLpo() ~3537, rejectExpense() ~3439). - ProjectCentreRequisitionController.php, ProjectCentrePurchaseOrderController.php, PettyCashProjectController.php, ProjectCasualController.php. - VehicleMaintenanceRequisitionController.php, VehicleCenterExpenseTypeController.php, VehicleCentreSupplierController.php, VehicleMaintenanceItemController.php, Admin/VehicleController.php, DriverExpenseRequestController.php. - BuildingController.php, BuildingCentreSupplierController.php, BuildingCentreItemController.php. - MiscellaneousPettyCashRequestController.php, Admin/UserController.php (send-money allow/revoke). - BudgetedExpenseController.php (pending/approve/disburse/failed ~L59-947), ScheduledExpenseController.php, TravelGrnRouteController.php, InterBranchTravelGrnRouteRateController.php, Admin/CasualPayDisbursementController.php. - PettyCashReconciliationRecordController.php (runVerification ~L256-295, close ~L311), PettyCashStatementController.php, DisbursementController.php, MpesaB2cTariffChargePeriodController.php, MpesaB2cTariffChargeController.php, MpesaB2cTopUpController.php, MpesaBalanceTrackerController.php, FilingRequestController.php. - PettyCashReportController.php, PettyCashSummaryReportController.php, DailyPettyCashSummaryReportController.php, PettyCashStatementReportController.php, PettyCashDashboardController.php, ProjectsDashboardController.php.

Services - app/Services/PettyCashDisbursementGlPostingService.php (GL disbursement posting; credit :31, debit-by-source :190-228). - app/Services/PettyCashLpoAccrualGlPostingService.php (voucher accrual DR/DR/CR + wa_supp_trans, :227-318). - app/Services/BudgetedExpenseTravelGrnVehicleRevenueRecorder.php (auto budgeted-expense generation :190-314).

Migrations (database/migrations/) - 2025_03_19_121540_create_petty_cash_purchase_orders_table.php:13-44 (requisition spine); 2025_03_19_121550_create_petty_cash_purchase_order_items_table.php:13-34. - 2025_04_12_201641 (rejection cols), 2026_02_27_140001 (tax-invoice approval), 2026_04_02_150311 (voucher_approved_by). - 2024_02_27_031640_create_payment_vouchers_table.php:14-24; 2024_02_27_031806_create_payment_voucher_items.php + 2024_06_23_171511:12-13 (polymorphic payable_type/payable_id). - 2024_06_08_090135_create_wa_petty_cash_requests_table.php:14-35 (legacy, initial/final approval), 2024_06_08_092433_create_wa_petty_cash_request_items_table.php. - 2025_03_18_153455 (budgeted_expenses), 2025_04_04_062251 (travel_grn_routes), 2025_04_21_123643 (inter_branch_travel_grn_route_rates), 2025_03_18_131120 (scheduled_expenses). - 2025_02_26_150708 (petty_cash_statements), 2025_04_17_103823 (mpesa_b2c_top_ups), 2025_04_15_134136 (mpesa_b2c_tariff_charge_periods), 2025_04_15_144010 (mpesa_b2c_tariff_charges).