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:
- 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.
- 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 ofapp/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) andclosea 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, withresendFailed. Project casuals are a parallel path onProjectCentreRequisitionController(:122-132). - Document Management (
FilingRequestController,:281-285): disbursed requisitions raise a filing request ("Ready For Filing") for their receipts/vouchers;fileDocumentmoves 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, altered2024_06_23_171511:12-13) — the polymorphic link:payable_type(default'invoice', but also petty-cash values) +payable_id(renamed fromwa_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 awa_supp_transsupplier-bill record is created alongside.
The chapter brief flagged
payment_voucher_items.payable_typeincludingpetty_cash_lpo— the migration proves the polymorphic column exists and defaults to'invoice'; the exact petty-cashpayable_typestring 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 flagsapproved/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-feesroute 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, polymorphicparent_id/parent_type(e.g. abudgeted_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-dayrecord withverified,closed,comments; verified by matchingdisbursements.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 viaPettyCashDisbursementGlPostingService(credit petty-cash control54000-001, debit expense-by-source) or, for voucher-paid accruals,PettyCashLpoAccrualGlPostingService(DR expense / DR VAT / CR creditors +wa_supp_trans). The disbursement GL posting mirrorsDarajaDisbursementController::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 samepayment_vouchers/payment_voucher_itemspolymorphic tables that AP uses;payable_type/payable_idcarry the petty-cash requisition. Awa_supp_transsupplier bill is raised, so petty-cash LPOs become creditor liabilities exactly like AP invoices. The chart-of-account resolution reusesSupplierChartOfAccountService. - 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
BudgetedExpenseTravelGrnVehicleRevenueRecorderhooks into GRN receive (WaReceivePurchaseOrder/ConfirmedReceiveOrderController) and inter-branch transfers (NWaInventoryLocationTransfer) to auto-createbudgeted_expensespriced fromtravel_grn_routes.mileage_amountandinter_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/VehicleAssetAmortizationrecord (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-branchhelpers). Send Money rights are granted per-user viaUserController::allow/revokeSendingMiscellaneousPettyCash. - Task Center. Requisition review/disburse steps register tasks (keys like
petty_cash_vehicle_requisition_review/_disburse) validated throughTaskCenterService— 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-357vs:82-105). - State machine lives in
petty_cash_purchase_orders.stage, values fromapp/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: credit54000-001, debit expense-by-source inPettyCashDisbursementGlPostingService(~L31, L190-228); accrual DR/DR/CR inPettyCashLpoAccrualGlPostingService. - Budgeted expenses auto-generated from GRN receive in
BudgetedExpenseTravelGrnVehicleRevenueRecorder(~L190-314); tablesbudgeted_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); permpetty_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_typestring written intopayment_voucher_items(brief mentionedpetty_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-approvalstates point to. - Permission seed source — the authoritative definition of the
*___viewstrings (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/ProjectCasualPayenum 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).