Skip to content

Cross-Module Handoff Map — How the Whole ERP Is Wired Together

Bizwiz Guide · Capstone · Review instrument The thirty chapters each documented one module in isolation. This page is the stitch: it follows value as it crosses module boundaries — a sale from a shop's doorstep to a line in the trial balance, a purchase from "we're low on stock" to a cheque, a truck's fuel from the pump to the P&L, a month of work from a punch-clock to a bank transfer — and names the exact table, service, or key at every seam. Read the chapters to learn what each module does; read this to learn how they hand off to each other.

The one-paragraph version. Bizwiz has one general ledger (wa_gl_trans, Book 4) that every value-moving module posts into as signed legs that must net to zero, and one inventory ledger (wa_stock_moves, Book 2) that every physical movement writes to. Sales (Book 1) push receivables in and stock out; procurement (Book 3) pulls stock in and payables up; the fleet (Book 5) and payroll (Book 6) are large money-out feeders; and everything settles through two shared engines — the polymorphic payment-voucher (payment_voucher_items, Book 3) for anything owed to a supplier-like party, and the payroll calculation/posting services (Book 6) for anything owed to staff. Wrapped around all of it sits the control plane (Book 7): permissions decide who may act, two feature-flag tables decide which version of each flow runs for this tenant, number series stamp every document, dimensions tag every ledger row, approver limits set the thresholds on the approval queues that appear at nearly every seam, and the SmsService backbone narrates the whole thing to people's phones.


The master flow

flowchart TD
    subgraph CONTROL["Book 7 — Control plane (wraps everything below)"]
      direction LR
      PERM[Permissions & roles<br/>user_permissions · method-level gate]
      FLAG[Feature flags<br/>settings + administration_settings]
      NUM[Number series<br/>wa_numer_series_codes]
      DIM[Dimensions<br/>branch/dept/project/gl_tag]
      LIM[Approver limits<br/>approver_limits]
      SMS[SmsService backbone<br/>103 scenarios · SMS/WA/email]
    end

    subgraph SELL["MONEY IN — Sell → Collect → Bank (Book 1)"]
      CUST[Customer + Route<br/>Ch4/Ch5] --> ORD[Order → Invoice<br/>Ch1]
      ORD --> DISP[Dispatch → Delivery<br/>Ch2]
      POS[POS cash sale<br/>Ch3]
      DISP --> DEBT[(wa_debtor_trans<br/>receivable)]
      DEBT --> BANK[Banking & recon<br/>Ch6]
      POS --> BANK
    end

    subgraph BUY["MONEY OUT — Need → Buy → Receive → Owe → Pay (Book 3)"]
      REQ[Requisition<br/>wa_external_requisitions · Ch12] --> LPO[LPO<br/>wa_purchase_orders · Ch12]
      LPO -.supplier acts.-> PORTAL[Supplier Portal · Ch14]
      LPO --> GRN[GRN receive<br/>wa_grns · Book 2 Ch9]
      GRN --> SINV["Supplier invoice<br/>(wa_supp_trans) · Ch13"]
    end

    subgraph INVSPINE["Book 2 — Inventory ledger"]
      STOCK[(wa_stock_moves<br/>every physical movement)]
    end

    subgraph FLEET["MONEY OUT — Fleet (Book 5)"]
      FUEL[Fuel invoice · Ch19] 
      TYRE[Tyre invoice · Ch20]
      SVC[Service via petty-cash LPO · Ch21]
    end

    subgraph PEOPLE["MONEY OUT — Payroll (Book 6)"]
      CALC[PayrollCalculationService] --> PMD[(payroll_month_details)]
      PMD --> PPOST[PostPayrollGLService<br/>PM-##### per branch]
      CASU[Casuals pay · Daraja B2C]
    end

    VOUCH[[payment_voucher_items<br/>polymorphic voucher engine · Ch13]]
    PC[Petty cash · M-Pesa B2C<br/>Book 4 Ch17]
    FA[Fixed Assets · Ch16<br/>vehicle → asset → depreciation]

    GL[(wa_gl_trans<br/>SIGNED-AMOUNT TRIAL BALANCE<br/>every leg nets to zero · Book 4)]

    %% inventory seams
    ORD -->|COGS / stock out| STOCK
    GRN -->|stock in| STOCK
    STOCK -->|movement/variance/COGS| GL

    %% money-in seam
    BANK -->|DR bank/cash · CR debtors| GL
    DEBT -. sub-ledger .- GL

    %% procure-to-pay seams
    SINV --> VOUCH
    FUEL --> VOUCH
    TYRE --> VOUCH
    SVC --> PC
    PC --> VOUCH
    VOUCH -->|DR creditors · CR bank| GL
    SINV -. wa_supp_trans sub-ledger .- GL

    %% fleet ↔ assets
    FLEET -.vehicle.-> FA
    FA -->|FAC/FAD/DSP| GL

    %% payroll seam
    PPOST --> GL
    CASU -->|DR wages · CR bank| GL

    %% control-plane overlay
    CONTROL -. gates / flags / numbers / limits / notifies .-> SELL
    CONTROL -.-> BUY
    CONTROL -.-> FLEET
    CONTROL -.-> PEOPLE

    %% the still-open loop
    FLEET -. driver INCENTIVE earnings<br/>NOT found reaching payroll .-> PMD

The dashed line from Fleet to payroll_month_details is the one loop this guide could not close — see § Open loops at the bottom.


Arc 1 — Money in: a sale becomes a ledger entry

The revenue chain is the ERP's front door, and it crosses four books before it balances.

  1. Setup (Book 1 Ch4/Ch5 → control plane). A shop exists as a two-level customer record with a credit limit; a route assigns that shop to a salesman's plan. Feature flags decide whether onboarding needs a phone/KRA PIN and whether route-customer changes need approval (Book 7).
  2. Order → Invoice (Ch1). A route order (mobile) or backend order becomes a confirmed invoice. Which path runs — the 3-stage sales-order flow vs the legacy direct invoice — is a per-tenant flag (identify-sales-orders-from-normal-orders). The invoice stamps a number from wa_numer_series_codes and, on confirmation, depletes stock into wa_stock_moves (Book 2) and posts COGS + a receivable into wa_gl_trans (Book 4): DR Debtors control, CR Sales + Output VAT.
  3. Dispatch → Delivery (Ch2). A loading sheet dispatches the invoice; gate-pass and (flag-gated) delivery OTP/geo-fence confirm it reached the shop. The receivable now sits in wa_debtor_trans.
  4. Collect & bank (Ch6). Route banking and POS cash banking (Ch3) flow into the money-in matching engine: statement → match → verify → approve → post to GL (DR Bank/Cash control, CR Debtors control). This is where the receivable is extinguished. Every stage is an approval queue governed by permissions and, implicitly, by the trust model behind approver limits.

The seam that matters: Book 1 owns matching and the posting handoff; Book 4 owns the ledger table and the chart of accounts. Two different "GL Reconciliation" screens exist (banking-side in Ch6, finance-side in Ch15) — a documented name-collision, not a duplicate.


Arc 2 — Money out: procure-to-pay

The mirror image of Arc 1, and the template every other money-out flow imitates.

  1. Need → order (Book 3 Ch12). A branch requisition (wa_external_requisitions) is resolved into an LPO (wa_purchase_orders), which climbs a multi-level approval ladder. Approval thresholds are exactly the approver-limits surface from Book 7.
  2. Supplier acts (Ch14). The Supplier Portal lets the supplier accept/change/return the LPO — writing back to wa_purchase_orders and, on delivery, triggering receipts.
  3. Receive (Book 2 Ch9). The GRN (wa_grns) is the physical hinge: wa_grns.wa_purchase_order_id ← the PO. Goods land in wa_stock_moves (stock in) and the accrual posts to GL. Under ENFORCE_DELIVERY_OFFLOADING_FOR_INBOUND_DELIVERIES, the inbound-logistics offloading gate (Book 5 Ch22) can block this receive until offloading is complete — a fleet→inventory seam.
  4. Owe (Ch13). The GRN is matched to a supplier invoice, creating the creditor bill in wa_supp_trans.
  5. Pay (Ch13). A payment voucher settles it through the polymorphic payment_voucher_items engine → wa_bank_trans (Equity host-to-host EFT / cheque / safe) + wa_gl_trans (DR Creditors, CR Bank).

The seam that matters: that voucher engine is the single most reused abstraction in the ERP. Nine-plus document types settle through it by payable_type — which is why fleet fuel, tyres, petty cash, consumables and property all reach the bank the same way (Arc 3).


Arc 3 — Money out: the fleet, through three guarded seams

The fleet (Book 5) is a major money-out feeder, but — true to Bizwiz's "purpose-built vertical" habit — each spend type keeps its own tables and touches the shared ledgers at exactly one guarded point:

  • Fuel (Ch19) → WaSuppTran + three GL legs (Cr creditors / Dr fuel-expense / Dr VAT input), then settles as payment_voucher_items.payable_type = 'fuel'. Same voucher engine as Arc 2.
  • Tyres (Ch20) → a self-contained tyre sub-ERP that touches the ledgers only at invoice time: WaSuppTran + Dr Tyre-Asset / Dr VAT / Cr Creditors. It throws if those GL accounts aren't configured — a guarded seam, not a silent one.
  • Service (Ch21) → routes through petty cash (Book 4 Ch17) via a petty-cash LPO carrying vehicle_service_record_id, posting VehicleCost lines. Workshop spend reaches GL through the petty-cash engine rather than direct AP.

And the fleet also crosses into the balance sheet: a Vehicle (Ch18) is polymorphically onboarded as a Fixed Asset (assetable_type='App\Vehicle', Book 4 Ch16) — today the only fully-wired asset type — and then depreciates (FAD journals) back into wa_gl_trans. (Watch the look-alike: a vehicle can also carry vehicle_asset_amortizations, a separate vehicle-scoped depreciation that is not the fixed-asset link. Which is authoritative per tenant is open.)


Arc 4 — Money out: a month of work becomes one journal

Payroll (Book 6) is the single largest recurring money-out event, and it collapses the entire HR book into one calculation and one posting:

  1. Assemble inputs. The employee master (Ch23), attendance/leave (Ch25, which prorates gross pay by an attendance ratio), statutory rate tables (Ch24), and every obligation — advances, penalties, HELB, medical, benevolent, Sacco (Ch26) — converge on PayrollCalculationService.
  2. Calculate. Statutory amounts are written as columns; every other deduction is a capped pivot row in payroll_month_detail_deductions, keyed by a Deduction.code. Result: payroll_month_details.
  3. Post (the seam). On HR approval, PostPayrollGLService posts once per branch into wa_gl_trans (PAYROLL series, doc PM-#####): DR gross, CR net, CR each statutory/scheme liability. No HR sub-controller writes the GL itself — Sacco and every scheme reach the ledger only through this service.
  4. Casuals pay out separately over Daraja B2C (MpesaDisbursementService), booking DR wages / CR bank on the M-Pesa callback — the same live Daraja rail as Book 4 petty cash.

The seam that matters — and a resolved cross-book loop: the driver penalties raised across the fleet (fuel/mileage/short-banking/late-shift) are minted as polymorphic EmployeePenalty rows by their source modules, then a second HR approval turns each into a DED-EMPLOYEE-PENALTY pivot row that payroll deducts. That closes the penalty half of the Book 5 driver-comp loop. The incentive/earnings half does not — see below.


The hub: one ledger, one inventory spine

Two tables absorb almost everything above:

  • wa_gl_trans (Book 4) — the signed-amount trial-balance spine. Written only through GlTransactionService (40+ *GlPostingService classes). Every arc lands here: sales, banking, AP, fuel, tyres, petty cash, fixed-asset depreciation, payroll. Sub-ledgers (wa_banktrans / wa_debtor_trans / wa_supp_trans) are kept in step; a Utility submenu hunts the drift when they aren't. Whether a tenant runs extensive (per-category, per-branch) or default posting is the flag (use-extensive-gl-posting) that changes the legs of nearly every journal above.
  • wa_stock_moves (Book 2) — every physical movement: sales deplete it, GRNs replenish it, transfers move it between branches, adjustments/breaks correct it, and (if Manufacturing is ever switched on — see placeholders) production would consume and yield through it.

If you remember two table names from this entire guide, remember these two.


The control plane overlay (Book 7 touches every arc)

Nothing above runs unwrapped. For every seam in Arcs 1–4:

  • Permissions (user_permissions, module___action) gate the action — enforced in the controller method, not route middleware, so coverage is a hand-written and therefore auditable thing (Book 7 Ch27).
  • Feature flags (settings + administration_settings) decide which branch of the flow is live for this tenant — the "which is live per client" question raised in every single book resolves to one of these two tables.
  • Number series (wa_numer_series_codes) stamp every document the arcs create — invoices, LPOs, GRNs, vouchers, FAC/FAD/DSP, PM-#####.
  • Dimensions (branch / department / project / gl_tag) scope and tag the ledger rows, which is what makes per-branch posting and per-branch payroll journals possible.
  • Approver limits (approver_limits, level → threshold) parameterise the approval queues that sit at nearly every seam above — the richest "make users approvers, not curators" surface in the ERP.
  • The SmsService backbone (103 SmsScenarios + ScenarioNotificationDispatcher) narrates state changes and approvals to people across SMS / WhatsApp / email, from ~146 call sites.

The seam table (every cross-book handoff, keyed)

From To The handoff object / service Key
Sales invoice (B1 Ch1) Inventory (B2) stock depletion wa_stock_moves
Sales invoice (B1 Ch1) GL (B4) *GlPostingService wa_gl_trans (DR Debtors / CR Sales+VAT)
Delivery (B1 Ch2) Receivables (B1 Ch6) debtor balance wa_debtor_trans
Banking recon (B1 Ch6) GL (B4) post-on-approve wa_gl_trans (DR Bank / CR Debtors)
LPO (B3 Ch12) GRN (B2 Ch9) receive against PO wa_grns.wa_purchase_order_id
Inbound offloading (B5 Ch22) GRN (B2 Ch9) offloading gate (flag) offloading_end_time blocks receive
GRN (B2 Ch9) AP (B3 Ch13) invoice match wa_supp_trans
AP + fuel + tyre + petty cash Bank/GL (B4) polymorphic voucher payment_voucher_items.payable_type
Fuel invoice (B5 Ch19) AP/GL supplier invoice WaSuppTran + 3 GL legs, payable_type='fuel'
Tyre invoice (B5 Ch20) AP/GL supplier invoice (guarded) WaSuppTran, Dr Tyre-Asset/VAT/Cr Creditors
Service (B5 Ch21) Petty cash (B4 Ch17) petty-cash LPO vehicle_service_record_id → VehicleCost
Vehicle (B5 Ch18) Fixed Assets (B4 Ch16) polymorphic onboarding assetable_type='App\Vehicle'
Fixed Assets (B4 Ch16) GL (B4 Ch15) depreciation journal wa_gl_trans (FAC/FAD/DSP)
Fleet penalties (B5) Payroll (B6) polymorphic penalty + 2nd approval EmployeePenalty → DED-EMPLOYEE-PENALTY
Payroll month (B6) GL (B4) PostPayrollGLService wa_gl_trans (PM-#####, per branch)
Casuals pay (B6 Ch23) GL (B4) Daraja B2C callback DR wages / CR bank
Supplier credit note (B3) Debtors (B1) cross-ledger bridge affects_debtors=true → wa_debtor_trans
Every arc Control plane (B7) permissions / flags / numbers / limits / SMS see overlay above

Open loops carried into the AI phase

The guide resolved most of its cross-book questions (permission enforcement, the two flag tables, the shared voucher engine, the fleet-penalty→payroll seam). Three genuinely-open items remain, and they belong to whoever picks up the AI-leverage work:

  1. Driver INCENTIVE earnings never traced to the payslip (the dashed line above). Approved driver_tyre_incentives (and fuel/service incentives) flip an incentive_status in Book 5, but no code was found posting them into payroll_month_detail_earnings. The penalty half of the driver-comp loop is closed via EmployeePenalty; the earnings half is open. Either it's paid through a separate "Send to Accounts" payout outside payroll, or the loop is genuinely unfinished. This is the single most important seam to confirm with a human — it's real money owed to drivers.
  2. Permission-enforcement coverage. Enforcement is per-controller-method and hand-written; only a handful of ~60 admin controllers were verified to actually call the check. Which actions are reachable by URL regardless of $my_permissions is an authorization-gap audit, not just a doc gap.
  3. "Which is live per client" is now answerable but not yet answered. Every flag branch this guide documented in both directions resolves to settings / administration_settings — but the guide never enumerated, per tenant, which way each flag is actually set. A feature-flag inventory pass would turn "the ERP can do X or Y" into "this distributor does X" — the fact the AI phase most needs.

What this sets up

With the handoff map complete, the guide has delivered what it was built to deliver: a module-by-module, seam-by-seam account of how Bizwiz actually works, separating code-proven fact from flagged inference throughout, ready to validate with real users and devs. The recurring shape it surfaced — one durable spine per domain (GL, stock, vehicle, payroll, voucher), many feeders; approval queues at nearly every seam; behaviour switched per tenant by two flag tables; modern service-classes layered over a legacy wa_* core — is the terrain the AI-leverage phase now gets to work on.

And it points straight at the five stakeholder desires this project started from: - "Approvers, not curators" → the approval queues at every seam, already carrying maker-checker audit trails, governed by approver_limits. - Explainability & predictability → one ledger and one inventory spine mean every number is traceable to its legs; the SmsScenarios surface is ready to explain each event to the right person. - Dynamic reporting/navigation → dimensions + the flag inventory give the vocabulary to reason about any tenant's actual configuration. - Better algorithms → the fleet compliance layer (Book 5 Ch21) and the routing/scheduling surfaces are the flagged homes for ML routing, shortest-path, and anomaly detection.

The guide is complete. The research it was built to found begins here.