Routing Low-Income Assistance Flags Automatically

Once an account’s eligibility is known, a single question decides whether the bill is right: where does the flag go? This page is the dispatch layer that sits under the Assistance Program Eligibility Taxonomy, inside Municipal Utility Billing Architecture & Rate Taxonomy. The failure mode it resolves is the mis-route: a residential hardship credit computed with float that reconciles a cent off, a percentage discount handed to a commercial account that is not entitled to a direct rate reduction, or a flagged account whose rate structure failed to resolve and so gets billed at full price with no trace. The fix is a router that maps (customer_class, assistance_flag) to exactly one action through an explicit table, computes any credit with decimal.Decimal and a hard cap, and refuses to guess — every unmapped or degraded case falls to a deferred queue or manual review, never to a silent full-price bill.

Prerequisites

The imports and the data contract this router assumes — nothing else.

from __future__ import annotations
from dataclasses import dataclass
from decimal import Decimal, ROUND_HALF_UP
from enum import Enum
import hashlib
  • Python 3.11+ for the X | None union syntax and modern enum semantics; no third-party runtime is required for the router itself.
  • decimal.Decimal for every money value — the base charge, the credit, and the posted charge. A percentage discount computed in binary float drifts, and thousands of off-by-a-cent bills are exactly what reconciliation cannot close.
  • A resolved eligibility flag as input. This router runs after status resolution: the ACTIVE/GRACE decision, effective-date and grace-window math, and income verification are already handled upstream by the parent taxonomy. Here the flag is a settled fact; routing decides the billing consequence.
  • A resolved base charge. The rate engine has already priced consumption for the service period following Step-Rate vs Block-Rate Structure Design; the router adjusts that resolved charge, it does not re-rate usage.

Annotated Implementation

The router below is the whole dispatch layer. It classifies the account with Customer Class & Service Tier Mapping, looks up the single permitted action in a deterministic table, computes any credit with a cap so a household is never credited below zero, and seals every decision into an audit record before it leaves the function.

Routing a resolved assistance flag to exactly one billing action A resolved assistance flag and an already-priced base charge enter the router. Three ordered rules decide one outcome: a NONE flag passes the charge through untouched; a hit in the routing table on the (customer class, assistance flag) pair yields either a residential capped credit or a deferred-payment plan; a non-residential account carrying a rate-reduction flag is diverted by the policy guard to deferral, and any pair still unmapped is escalated to manual review. The four terminal actions are Passthrough, Apply capped credit (posted equals base minus the credit capped at base), Defer payment (the full charge stands), and Manual review. Every terminal action then converges on a single seal step that emits a SHA-256 integrity hash inside a tamper-evident RoutingResult. Resolved flag + base charge settled upstream · already priced route_assistance_flag(ctx) 1 · flag = NONE → passthrough, charge untouched 2 · ROUTING_TABLE[(class, flag)] → residential credit or deferral 3 · non-residential rate flag → defer · unknown pair → manual review Passthrough charge unchanged credit = 0.00 Apply capped credit posted = base − credit credit = min(raw, base) Defer payment full charge stands arrears plan · any class Manual review gap made visible never guessed flag = NONE residential credit deferred flag · guard unmapped pair Seal · SHA-256 integrity_hash tamper-evident RoutingResult

Figure: One resolved flag, one action — the router converges every branch, including the deliberate class guard, onto a single hash-sealed audit record.

CENTS = Decimal("0.01")


class CustomerClass(str, Enum):
    RESIDENTIAL = "residential"
    COMMERCIAL = "commercial"
    INDUSTRIAL = "industrial"


class AssistanceFlag(str, Enum):
    LIHEAP_DISCOUNT = "liheap_discount"        # low-income percentage credit
    MEDICAL_BASELINE = "medical_baseline"      # fixed exempt-allowance credit
    DEFERRED_PAYMENT = "deferred_payment"      # arrears plan, no rate change
    NONE = "none"                              # no assistance on this account


class RouteAction(str, Enum):
    APPLY_CREDIT = "apply_credit"
    DEFER_PAYMENT = "defer_payment"
    PASSTHROUGH = "passthrough"
    MANUAL_REVIEW = "manual_review"


# Exactly one action per (class, flag). The table is the policy: a direct rate
# reduction is a residential customer-protection instrument, so commercial and
# industrial hardship flags are deliberately absent and fall through to a guard
# below rather than silently receiving a credit they are not entitled to.
ROUTING_TABLE: dict[tuple[CustomerClass, AssistanceFlag], RouteAction] = {
    (CustomerClass.RESIDENTIAL, AssistanceFlag.LIHEAP_DISCOUNT): RouteAction.APPLY_CREDIT,
    (CustomerClass.RESIDENTIAL, AssistanceFlag.MEDICAL_BASELINE): RouteAction.APPLY_CREDIT,
    (CustomerClass.RESIDENTIAL, AssistanceFlag.DEFERRED_PAYMENT): RouteAction.DEFER_PAYMENT,
    (CustomerClass.COMMERCIAL, AssistanceFlag.DEFERRED_PAYMENT): RouteAction.DEFER_PAYMENT,
    (CustomerClass.INDUSTRIAL, AssistanceFlag.DEFERRED_PAYMENT): RouteAction.DEFER_PAYMENT,
}


@dataclass(frozen=True, slots=True)
class BillingContext:
    """Everything the router needs, already resolved upstream. Frozen so a
    routing decision can never mutate the inputs it was judged against."""
    account_id: str
    customer_class: CustomerClass
    assistance_flag: AssistanceFlag
    base_charge: Decimal                       # resolved by the rate engine
    discount_pct: Decimal = Decimal("0")       # e.g. Decimal("0.25") for 25%
    baseline_credit: Decimal = Decimal("0")    # fixed $ value of exempt volume


@dataclass(frozen=True, slots=True)
class RoutingResult:
    account_id: str
    action: RouteAction
    posted_charge: Decimal                     # what the ledger receives
    credit_applied: Decimal
    integrity_hash: str


def _to_money(value: Decimal) -> Decimal:
    # Quantize once, at the boundary, so no fractional cent survives to the GL.
    return value.quantize(CENTS, rounding=ROUND_HALF_UP)


def _credit_for(ctx: BillingContext) -> Decimal:
    # One rule fires per flag; the credit is capped at the gross charge so a
    # subsidy can never invert the bill into a negative (a refund it is not).
    if ctx.assistance_flag is AssistanceFlag.LIHEAP_DISCOUNT:
        raw = ctx.base_charge * ctx.discount_pct
    elif ctx.assistance_flag is AssistanceFlag.MEDICAL_BASELINE:
        raw = ctx.baseline_credit
    else:
        raw = Decimal("0")
    return min(raw, ctx.base_charge)


def _seal(account_id: str, action: RouteAction, posted: Decimal, credit: Decimal) -> str:
    # Tamper-evident: any later edit to the routed amounts changes the digest,
    # so the audit log proves the posted charge was never altered after the fact.
    basis = f"{account_id}|{action.value}|{posted}|{credit}"
    return hashlib.sha256(basis.encode("utf-8")).hexdigest()


def route_assistance_flag(ctx: BillingContext) -> RoutingResult:
    """Dispatch a resolved assistance flag to exactly one billing action."""
    # 1. No assistance -> the charge passes through untouched.
    if ctx.assistance_flag is AssistanceFlag.NONE:
        posted = _to_money(ctx.base_charge)
        return RoutingResult(
            ctx.account_id, RouteAction.PASSTHROUGH, posted, Decimal("0.00"),
            _seal(ctx.account_id, RouteAction.PASSTHROUGH, posted, Decimal("0.00")),
        )

    # 2. Policy guard: a non-residential account carrying a rate-reduction flag
    #    is a mis-classification, not a discount. Route it to a deferred plan
    #    (which every class may use) instead of applying an unentitled credit.
    action = ROUTING_TABLE.get((ctx.customer_class, ctx.assistance_flag))
    if action is None and ctx.customer_class is not CustomerClass.RESIDENTIAL:
        action = RouteAction.DEFER_PAYMENT

    # 3. Anything still unmapped (unknown class/flag pair) is escalated, never
    #    guessed — the whole point of the router is to make gaps visible.
    if action is None:
        action = RouteAction.MANUAL_REVIEW

    if action is RouteAction.APPLY_CREDIT:
        credit = _to_money(_credit_for(ctx))
        posted = _to_money(ctx.base_charge - credit)
    else:
        # DEFER_PAYMENT and MANUAL_REVIEW change the bill's *handling*, not its
        # amount: the full charge stands while an arrears plan or a caseworker
        # takes over, so no credit is posted here.
        credit = Decimal("0.00")
        posted = _to_money(ctx.base_charge)

    return RoutingResult(
        ctx.account_id, action, posted, credit,
        _seal(ctx.account_id, action, posted, credit),
    )

The load-bearing detail is the ordering of the three fall-throughs: the routing table encodes what is allowed, the policy guard converts a non-residential rate-reduction flag into a deferred plan rather than a credit, and only a genuinely unknown pair reaches MANUAL_REVIEW. A flagged account can therefore never leave this function at full price without also being tagged for deferral or review — the silent full-price bill is structurally impossible. A DEFER_PAYMENT result then feeds arrears handling, and any account that could not resolve its rate at all is handed to Fallback Routing for Missing Rate Data before it ever reaches this router.

Edge Cases and Billing Gotchas

  • Commercial account carrying a residential hardship flag. A data-entry error or a mis-migrated ERP record can attach a LIHEAP_DISCOUNT to a small commercial account. The routing table has no entry for that pair, so the policy guard sends it to DEFER_PAYMENT (a plan any class may use) instead of applying a rate reduction the account is not entitled to — preventing the cross-subsidization that surfaces in a quarterly audit as an unexplained revenue gap.
  • Discount exceeds the gross charge. A low-usage month can leave a base charge smaller than the computed percentage or baseline credit. min(raw, ctx.base_charge) caps the credit at the gross charge so the posted amount floors at 0.00; a subsidy reduces a bill, it never becomes a cash refund the router was never authorized to issue.
  • Rounding drift on a percentage credit. base_charge * discount_pct produces long decimals. Quantizing the credit and the posted charge exactly once, at the boundary, with ROUND_HALF_UP keeps the two internally consistent (credit + posted == base_charge) so reconciliation against the Data Governance & Privacy Compliance ledger never breaks by a cent.
  • Unknown class/flag pair after a program change. When a new program tier ships upstream but the routing table has not yet been extended, the pair is unmapped and the account routes to MANUAL_REVIEW rather than defaulting to a credit or a passthrough. The gap is loud by design, and the audit hash records exactly which accounts were held.

Verification Snippet

Prove the two invariants that keep the ledger whole: a credit never exceeds the gross charge, and a non-residential rate-reduction flag never receives a credit. Both assert with Decimal equality — never float tolerance.

def test_capped_credit_and_class_guard():
    # Residential LIHEAP: 25% off an exact charge posts to the cent.
    res = route_assistance_flag(BillingContext(
        account_id="ACC-00010001",
        customer_class=CustomerClass.RESIDENTIAL,
        assistance_flag=AssistanceFlag.LIHEAP_DISCOUNT,
        base_charge=Decimal("120.00"),
        discount_pct=Decimal("0.25"),
    ))
    assert res.action is RouteAction.APPLY_CREDIT
    assert res.credit_applied == Decimal("30.00")     # exact, not 29.999...
    assert res.posted_charge == Decimal("90.00")
    assert res.credit_applied + res.posted_charge == Decimal("120.00")

    # Commercial account with the same flag: NO credit, routed to deferral.
    com = route_assistance_flag(BillingContext(
        account_id="ACC-00020002",
        customer_class=CustomerClass.COMMERCIAL,
        assistance_flag=AssistanceFlag.LIHEAP_DISCOUNT,
        base_charge=Decimal("120.00"),
        discount_pct=Decimal("0.25"),
    ))
    assert com.action is RouteAction.DEFER_PAYMENT
    assert com.credit_applied == Decimal("0.00")
    assert com.posted_charge == Decimal("120.00")
    assert len(com.integrity_hash) == 64

Run the same suite against a corpus of hand-verified fixtures before every deployment. A single mismatch means a table, guard, or rounding regression that would otherwise ship as thousands of mis-routed bills.

Frequently Asked Questions

Why route on a table instead of a chain of if/elif statements?
A table makes the policy explicit and testable: every allowed (customer_class, assistance_flag) pair is one line you can audit at a glance, and any pair not present is, by definition, not permitted. A nested if/elif hides the same rules in control flow, where an accidental fall-through becomes a wrong credit. With the table, an unmapped pair deterministically routes to a deferred plan or manual review rather than to whichever branch happened to match.
What stops a commercial account from receiving a residential hardship discount?
The routing table simply has no entry that maps a non-residential class to a rate-reduction action. When the lookup misses and the class is not residential, the policy guard converts the flag into a deferred-payment plan — an instrument every class may use — instead of applying a credit. A direct rate reduction is a residential customer-protection measure, so extending it to a commercial account would be cross-subsidization, which the guard prevents structurally.
Can an assistance credit ever push a bill negative?
No. The credit is capped with min(raw_credit, base_charge) before it is applied, so the posted charge floors at 0.00. A subsidy reduces the amount owed; it is never a cash refund. If a program genuinely needs to issue money back, that is a separate refund workflow with its own authorization, not something the flag router is permitted to do.
Where does this router sit relative to eligibility resolution?
Strictly after it. Status resolution — income verification, effective and expiration dates, and the grace window — is handled upstream in the Assistance Program Eligibility Taxonomy and produces a settled flag. This router takes that flag and the already-priced base charge and decides the billing consequence: apply a capped credit, defer payment, pass through, or escalate. It never re-evaluates eligibility or re-rates consumption.
How is a routing decision made auditable?
Every result carries a SHA-256 integrity hash computed from the account id, the chosen action, the posted charge, and the credit applied. Re-hashing the archived decision during an audit proves the posted amount was never altered after the fact, giving the non-repudiation municipal finance audits require. Corrections are made by re-running the router and posting a separate delta, never by mutating a sealed decision in place.

Up one level: Assistance Program Eligibility Taxonomy · Parent guide: Municipal Utility Billing Architecture & Rate Taxonomy · Return to the utilitybilling.org home.