Adjustment Rules Guide
About the rules
What they are
Adjustment rules are the business logic behind the automatic bill-adjustment workflow (/adjust/[billNumber]). Each rule is a JSON record — stored in src/lib/adjustment-rules/rules.json and evaluated by json-rules-engine — that says: *"if this charge line matches these conditions, recommend this credit."* Rules are authored and edited in the Rules Workbench (/adjust/rules), not in code.
A rule is evaluated per bill line item, using these facts pulled from the bill:
| Fact | Meaning |
|---|---|
charge_code | The specific charge code on the line (e.g. DATA_OVERAGE) |
charge_category | The charge's category (e.g. USAGE, ROAMING, PLAN) |
amount | The line's dollar amount |
overage_gb / used_gb / included_gb | Usage figures, when the line has a usage_summary |
autopay_enabled / paperless_billing_enabled | Account-level flags |
Each rule also defines:
- ·`decision` —
auto_approve(system pre-clears the credit, one click
applies it) or csr_review (a human should confirm before applying). Either way, nothing changes on the bill until someone actually clicks "Apply credit" — there's no separate approval queue in this build.
- ·`recommended_amount_strategy` — how the credit dollar amount is
computed:
- ·
full_amount— credits the entire line - ·
fixed— credits a fixed dollar amount, regardless of charge size - ·
cap— credits the charge amount, capped at a configured maximum - ·
percentage_cap— credits a percentage of the charge, capped at $25 - ·`reason_code` / `reason_label` — which business reason this credit
falls under (from the adjustment_reason_reference table)
- ·`rationale` — the bullet points shown to the CSR explaining *why*
When applicable
The three rules currently configured:
| Rule | Fires when | Decision | Amount |
|---|---|---|---|
| Data overage — full credit | charge_code = DATA_OVERAGE and overage_gb > 0 | Auto-approve | Full charge amount |
| International roaming — CSR review | charge_category = ROAMING | CSR review | Capped at $15 |
| Data overage — goodwill review for large overage | charge_code = DATA_OVERAGE and overage_gb > 5 | CSR review | Capped at $50 |
Note the last two rows: any overage line above 5 GB satisfies both the first rule and the third rule at once. This is deliberate — it's the one place in the current rule set where two rules genuinely overlap.
How overlaps resolve today: when more than one enabled rule matches the same charge, an auto_approve result always wins over a csr_review result, *regardless of either rule's priority number*. If two matching rules land on the same decision, the one recommending the larger credit wins. In practice this means the large-overage goodwill-review rule is currently always overridden by the full-credit auto-approve rule whenever both match — the priority field only affects the order rules are evaluated internally, it does not decide which recommendation surfaces. This same explanation is shown live on the /adjust/[billNumber] page under "How adjustment decisions work." Whether overlap resolution should instead respect priority is an open decision, not yet implemented.
Synthetic bills info
Two dedicated test bills exist purely for exercising the overage rule in isolation, seeded by db-schema/021_seed_adjustment_rule_test_bills.sql on a dedicated account (BA-TEST-9001, customer CUST-TEST-9001, marked is_synthetic_test_data = true):
| Bill | Scenario | Expected result |
|---|---|---|
BILL-OVERAGE-RULE-PASS-9001 | DATA_OVERAGE line with overage_gb = 2.5 | Matches the full-credit rule — auto-approve candidate |
BILL-OVERAGE-RULE-FAIL-9002 | DATA_OVERAGE line with overage_gb = 0 | Matches no rule — zero candidates (negative control) |
Because these are flagged as synthetic test data, /adjust/[billNumber] shows a "Reset test bill" button for them — it permanently deletes every applied adjustment and credit/reversal line item on the bill and restores it to its original seeded state, so the same rule can be tested repeatedly without cleanup. This button is disabled (403) on every other bill on purpose, so it can never touch real billing data.
For scenarios the two dedicated test bills don't cover, the existing sample dataset (from 003_sample_billing_seed.sql) already has suitable charges — no new synthetic data was needed:
| Bill | Why it's useful |
|---|---|
BILL-4003 | Has an INTL_DAY_PASS (ROAMING) line — demonstrates the roaming CSR-review rule |
BILL-4005 | Has an 8.6 GB DATA_OVERAGE line — demonstrates the overlapping full-credit / goodwill-review rules together |
These are not marked as synthetic test data, so "Reset test bill" is not available on them — any adjustment applied there is a real, permanent action against the shared demo dataset.