Source Evidence
Where every statement in this guide came from. This page exists so a reader can check the guide rather than trust it — and so the next person to maintain it knows what was actually verified against a running system versus what was read off a design document.
What was inspected
Guide written against module version 1.4.0. Files read in the live module tree:
| Area | Read | Used for |
|---|---|---|
| Manifest | module.json | Version, slug, category, declared features, display metadata. |
| Configuration | All 11 files in config/ | Permissions, sidebar, routes, statuses, settings tab, audit events, API scopes. |
| Controllers | 19 files, ~15,500 lines | Which screens exist, which tabs each hub has, and which permission gates each action carries. |
| Models | 29 files, ~14,900 lines | Which tables are queried and how balances and aging are derived. |
| Libraries | Wu_bill_calculator, Wu_workflow, Water_utility_sms | The billing arithmetic, tier resolution and validation rules; the status-transition contract. |
| Migrations | 7 files | Table shapes, and which settings arrive with the schema. |
| Jobs | 4 files | Job slugs and cron schedules. |
| Views | 107 files across 17 folders | Confirming each documented screen and tab is really rendered. |
| Language | ~1,370 keys | The labels used in this guide match what the UI shows. |
| Specifications | 10 documents, ~8,800 lines under specs/ | Design intent and the reasoning behind decisions — always cross-checked against code. |
Facts confirmed against a running database
These were not taken from a document. They were read out of a live schema and a live seeded dataset, because each one is the kind of detail that quietly goes stale.
| Claim in this guide | How it was confirmed |
|---|---|
27 wu_ tables exist, with their exact columns and types | Read the live schema directly, table by table. |
| Every table carries a soft-delete flag and the audit block | Same schema read; confirmed on all 27. |
| 38 permission keys in 9 categories | Counted in config/permissions.php. |
| 14 module settings, and their shipped values | Read from the live settings table after migration, and cross-checked against the seeding migration. |
| Twelve status lifecycles and their exact edges | Read from config/statuses.php, which is the single definition the runtime uses. |
| Job slugs and cron schedules | Read from the job classes themselves, not from the manifest — the manifest's feature flags are documentation, not enforcement. |
| Demo bill amounts (₱439.00 / ₱235.00 / ₱2,330.00 / ₱235.00) and the run total ₱3,239.00 | Queried out of the database after seeding, then reconciled against the tier arithmetic by hand. |
| The commercial bill's five lines and the 13 cu.m billed above the top block | Queried the bill lines directly. |
| A flagged senior account produces no discount line today | Read the refusal reason recorded in that bill's own snapshot. |
| Both demo tariff versions pass the module's own validator | Ran the validator against the persisted tiers of both versions. |
Verification runs behind the demo dataset
The demo seeder documented in this guide was exercised end to end before publication.
| Check | Result |
|---|---|
| First run on an empty district | 85 rows created across 24 tables. |
| Immediate second run (idempotence) | 0 created, 0 changed, 85 already current — and a row-by-row comparison across all 24 tables came back byte-identical, including timestamps. |
| Coherence of the seeded chain | 43 assertions, all passing: every account resolves its customer and its tap; every bill resolves its run, account, reading and tariff version; every allocation resolves its receipt and its bill. |
| Money reconciliation | Run totals equal the sum of the bills; each bill's water charge equals the sum of its own water lines; the batch total equals the sum of the posted receipts; no bill is over-allocated. |
| Removal | 85 rows soft-deleted, physical row count unchanged — nothing was destroyed. |
| Second removal | 0 rows — a clean no-op. |
| Re-seed after removal | The same 85 rows restored in place, with no duplicates created. |
| Dry run, both directions | Reported the full plan and wrote nothing. |
Design decisions this guide relies on
A handful of choices explain most of the module's behaviour. They were confirmed in code, not just in the design documents.
| Decision | Consequence you can see |
|---|---|
| Customers are core records, not a module-owned table | A water customer is the same record as any other customer in the system — which is why the demo has to create six of them. |
| The tap, not the customer, is the stable entity | Ownership transfer is close-and-open. There is no "change owner" button, and that is deliberate. |
| Readings attach to a meter installation, not to a meter | A meter swapped mid-cycle still produces one correct bill, because each installation's span is computed on its own digits and multiplier. |
| Tariff versions are immutable once approved | No edit path exists from approved back to draft — a correction is a new version with a later effective date. |
| The billing engine is deliberately isolated from everything | It cannot read the database, the settings or the clock. Everything it needs is handed to it, which is what makes a bill reproducible years later from its snapshot alone. |
| Money defaults are inert, never invented | An unconfigured senior discount rate produces no line and records why, rather than a plausible-looking guess. |
| Penalty materialises once | The nightly job derives a penalty for display but never writes one; it becomes real exactly once, as a line on the next bill. That single-writer arrangement removes the likeliest source of duplicated money. |
| "Paid" is derived, never stored | Voiding a receipt makes its bill outstanding again immediately, with no second place that could disagree. |
| One status writer for the whole module | Every status change is checked against the legal moves, checked against the user's permission, and recorded — there is no back door. |
| Field Service is optional and fails soft | Service orders always work; the bridge only adds a mirror when it is switched on. |
Gaps and open questions found while writing
Recorded honestly, because a guide that only lists what works is not much use to whoever has to run the thing.
| Finding | What it means for you |
|---|---|
| The senior-citizen discount has no rate setting. The module has an enable flag but no place outside the tariff to put the statutory percentage and ceiling. | Configure the rate in your tariff. Until then, flagged accounts get no discount line — and the reason is recorded on each bill, so it is visible rather than silent. |
| Approving a tariff version starts out held by nobody. The permission is newer than the original set and is granted to no role by default. | Appoint a checker deliberately in the Roles editor. Until you do, versions can be drafted but not approved. |
| No SMS gateway ships with the platform. | OTP-by-SMS and text notices need a provider configured first. Email and printed service work regardless. |
| Meter-reading evidence is not included in the demo data. | Deliberate: an evidence row without a real file behind it produces a broken preview. Upload real photos to see that feature working. |
| Portal credentials are never seeded. | Also deliberate — fabricating them would ship a working customer login. Request a live OTP instead. |
| The demo tariff rates are invented. | They exist to show the LWUA tier shape working, not to be billed. Replace them before going live. |
How to re-verify this guide
If you are updating these pages after a change to the module, redo this:
Re-read the configuration
The 11 files in
config/are the fastest source of truth for permissions, screens, routes, statuses and audit events. Most of the reference page comes straight out of them.Confirm against the live schema
Table and column lists drift faster than any document. Read them from a migrated database.
Re-seed and re-verify
Run the demo seeder, then run it again and confirm nothing changed. Re-query the demo bill amounts and update this guide if they moved — a change there means the billing arithmetic changed.
Prefer code over specification
Where a specification and the code disagree, the code is what your district will actually experience. Say so on this page when you find one.