← all branches

feat/tax-regime

blockedviewing older commit
1bda877 · fullPR #302reviewed 2026-07-13 02:30 UTC12H · 12M · 9L
The branch
Purpose
Fix systematic over-taxation of northern-border sites on calculated bills — CFE scrapes read real IVA from recibos (CVEIVA field) but calculated bills (THOR, baselines, grid) hardcoded 16%.
Goal
Add per-contract tax_regime field (iva=16% / iva_frontera=8%) and wire it through the bill-compute path so border sites are taxed correctly.
Sub-goals
  • SG-1: Schema — tax_regime text NOT NULL DEFAULT 'iva' on utility_contracts (migration 0058, additive+safe)
  • SG-2: Domain — TAX_REGIMES/TaxRegime/TAX_REGIME_RATES constants, threaded through type/mapper/decisions/queries/insert
  • SG-3: Billing — resolveSiteTariffContext returns taxRegime; computeBillShell applies it; persistBillForContract resolves it from contract for pinned-tariff path
  • SG-4: Tests — tax-regime.test.ts (mapping) + bill-calculator 8% case; manual validation against WE LOVE BURGERS (16%) and PETCO (8%)
The changes (whole branch)
What
New column tax_regime on utility_contracts with safe DEFAULT 'iva'. Constants TAX_REGIMES/TAX_REGIME_RATES on both domain type and (separately) DB schema. Thread through mapper, CreateContractCommand, insert query. resolveSiteTariffContext now returns taxRegime. computeBillShell uses TAX_REGIME_RATES[taxRegime ?? 'iva'] replacing MX_IVA=0.16. persistBillForContract Strategy A adds a findById call to resolve taxRegime when tariff is pinned.
Why
IVA is per-location (franja fronteriza ~20km strip), not per-tariff/zone. Same GDMTH THOR × Baja California has 15 sites at 16% and 5 at 8%. Must live on the contract.
Areas
domains/cross-domain/src+294domains/utility/src+381packages/database+130500
Blast
12 non-generated files changed (+68/-5 excl. snapshot). Touches the bill-compute critical path, two auto-creation decision functions, and one cross-domain query. Migration is additive-safe.
Critical path: bill computation for THOR/CFE baselines/grid bills Migration 0058 is additive (DEFAULT 'iva' NOT NULL) — no backfill needed No API surface change — taxRegime not yet exposed via handler/schema
typecheck· PR author reports typecheck green across @batu/database, @batu/utility-domain, @batu/cross-domain, @batu/platformdb:check-rls· PR author reports db:check-rls green; migration is one clean additive lineci· CI checks not accessible via tokencoderabbit· No .coderabbit.yaml in repo

Findings · 33

correctness6

high

persistCalculatedBillShell pins tariff fields but never forwards taxRegime — border sites always 16%

domains/cross-domain/src/calculated-bill-persist.shells.ts:119

Same root cause as above. Fix: read ctx.value.taxRegime after resolveSiteTariffContext and pass it into the computeBillShell call.

high

UpdateContractCommand omits taxRegime — regime cannot be corrected on existing contracts

domains/utility/src/utility-contract/utility-contract.decisions.ts:72

All pre-existing contracts were migrated to 'iva' default. No code path lets operators correct 'iva_frontera' on them through normal API flow. Must be added to UpdateContractCommand + patch + decision + query.

medium

decideEnsureContractForTariff drops taxRegime on replacement

domains/utility/src/utility-contract/utility-contract.decisions.ts:452

Add taxRegime: existingContract.taxRegime to buildContractInput return in the CreateContractForTariff branch.

medium

Strategy A silently falls back to 16% if findById returns null

domains/cross-domain/src/bill-for-contract.shells.ts:149

A soft-deleted contract returns null; taxRegime becomes undefined; border bill is over-taxed with no error raised. Return explicit error for null contract.

low

TAX_REGIME_RATES lookup has no runtime guard — invalid DB value yields NaN

domains/cross-domain/src/bill-compute.shells.ts:184

Add a runtime guard or rely on DB CHECK constraint (preferred).

low

Strategy B (PPA) silently ignores taxRegime — no docs

domains/cross-domain/src/bill-for-contract.shells.ts:179

Document in PersistBillForContractInput that taxRegime is ignored for PPA contracts.

security7

high

No DB-level CHECK constraint — corrupt tax_regime produces NaN bill totals

packages/database/drizzle/0058_material_kitty_pryde.sql:1

text() with { enum } is TypeScript-only. A raw SQL UPDATE or future migration can write arbitrary text; TAX_REGIME_RATES[unknown] = undefined; undefined × subtotal = NaN persisted silently. Add CHECK (tax_regime IN ('iva','iva_frontera')) to migration.

high

Self-reported border rate — no geographic validation

domains/utility/src/utility-contract/utility-contract.decisions.ts:59

taxRegime is accepted from callers without checking the contract's pricing zone falls in the franja fronteriza. An operator can falsely claim 8% (iva_frontera) on a mainland site, under-paying IVA by 50%.

medium

TOCTOU: findById for taxRegime runs outside bill-write transaction

domains/cross-domain/src/bill-for-contract.shells.ts:151

Contract read and bill INSERT are not atomic. A concurrent regime update produces a bill with stale rate, idempotency-locked. Wrap in a single transaction or pass taxRegime from caller.

medium

Silent fallback to 16% when contract deleted mid-computation — no error or log

domains/cross-domain/src/bill-for-contract.shells.ts:149

Should be an explicit ContractNotFound error so callers can alert rather than persist an incorrect bill.

low

RLS contracts_update_admin WITH CHECK (true) — taxRegime writable if added to UpdateContractCommand

packages/database/src/schema/utility-contracts.ts:116

No column-level restriction. Document the dependency: UpdateContractCommand must never include taxRegime without a privileged endpoint.

low

savings-report-compute.shells.ts extra findById per contract

domains/cross-domain/src/savings-report-compute.shells.ts:229

Addressed by improvement finding — add taxRegime to SavingsSiteContract.

low

bill-pdf handler missing taxRegime override — parity gap

apps/platform/src/api/handlers/bill-pdf.handler.ts:104

computeBillShell supports taxRegime override but the PDF endpoint doesn't expose it.

conventions7

high

TAX_REGIMES const duplicated in domain type AND DB schema — violates one-directional type flow

packages/database/src/schema/utility-contracts.ts:41

TAX_REGIMES defined identically in utility-contract.type.ts and utility-contracts.ts. DB schema must import from domain, not redefine. A silent divergence compiles fine but breaks at runtime.

high

taxRegime not propagated in decideEnsureContractForTariff clone path

domains/utility/src/utility-contract/utility-contract.decisions.ts:366

buildContractInput clones fields from existingContract but omits taxRegime; replacement contract defaults to 'iva'. Border contracts lose their 8% regime on tariff reconciliation.

high

taxRegime not propagated in decideEnsureContractFromBills (CFE pipeline path)

domains/utility/src/utility-contract/utility-contract.decisions.ts:450

buildContractInput used by both CreateContractFromBills and TerminateAndReplace never sets taxRegime. Same loss on auto-replacement by the CFE pipeline.

medium

UpdateContractCommand omits taxRegime — no route to correct mis-set regime

domains/utility/src/utility-contract/utility-contract.decisions.ts:72

If intentional (immutable-after-creation), document it with a comment. If not, add to the command.

medium

Out-of-transaction contract read in Strategy A violates FCIS atomicity

domains/cross-domain/src/bill-for-contract.shells.ts:149

ADR-016: fetch → decide → write atomically. The findById for taxRegime is outside any transaction; the bill write is in a separate nested transaction. Not atomic.

low

type-check.ts not updated — implicitly correct but unverified

domains/utility/src/utility-contract/utility-contract.type-check.ts:22

AssertEqual covers taxRegime implicitly. Confirm typecheck passes.

low

TAX_REGIMES not re-exported from DB schema index — inconsistent with CONTRACT_STATUSES

packages/database/src/schema/index.ts:48

Either export both or neither.

tests7

critical

persistCalculatedBillShell does not thread taxRegime into computeBillShell — border bills taxed at 16%

domains/cross-domain/src/calculated-bill-persist.shells.ts:115

resolveSiteTariffContext now returns taxRegime but persistCalculatedBillShell forwards only tariffId/pricingZoneId/tariffCode/isTou to computeBillShell. computeBillShell's guard (`if tariffId === undefined...`) is false so the taxRegime assignment is skipped. TAX_REGIME_RATES[undefined ?? 'iva'] = 0.16 always. Every iva_frontera site billed through this shell is over-taxed. No test covers this path.

high

resolveSiteTariffContext taxRegime propagation never asserted

domains/cross-domain/src/site-tariff.queries.ts:184

Fixtures in site-tariff.test.ts never set taxRegime; the field is never asserted in returned SiteTariffContext. The JOIN column could be miswired with no failing test.

high

persistBillForContract tariff-strategy path has zero test coverage

domains/cross-domain/src/bill-for-contract.shells.ts:143

bill-for-contract.test.ts only tests the PPA pure mapper. The tariff strategy with the new findById lookup has no unit or integration test.

high

No integration test for iva_frontera contract → bill total end-to-end

domains/utility/src/utility-contract/__tests__/utility-contract.integration.test.ts:1

Manual validation against PETCO (8%) is documented in the PR but not automated. Any future refactor of the shell chain can silently break this.

medium

projectSiteTariffContext unit tests never assert taxRegime

domains/cross-domain/src/__tests__/site-tariff.test.ts:1

Add a parametrized case for taxRegime: 'iva_frontera' through the pure projection.

medium

bill-calculator test bypasses TAX_REGIME_RATES lookup wiring

domains/utility/src/bill-calculator/__tests__/bill-calculator.decisions.test.ts:63

calculateBill(gdmthInput({ taxRate: 0.08 })) passes rate directly; doesn't test TAX_REGIME_RATES['iva_frontera'] lookup.

low

No round-trip test for taxRegime: 'iva_frontera' through insert query

domains/utility/src/utility-contract/utility-contract.queries.ts:355

Add one integration test creating a contract with iva_frontera and reading it back.

improvement6

high

Extra findById per savings contract — taxRegime absent from SavingsSiteContract

domains/cross-domain/src/savings-contracts.queries.ts:93

resolveSiteContractsForSavings omits taxRegime from SELECT, causing a separate findById per contract on every savings compute. Add taxRegime to the SELECT + SavingsSiteContract + billInputFor to eliminate the redundant query.

medium

TAX_REGIMES duplicated — DB schema should import from domain type

packages/database/src/schema/utility-contracts.ts:41

Same fix as conventions finding above.

medium

taxRegime not in UpdateContractCommand — no API path to correct mis-set regime

domains/utility/src/utility-contract/utility-contract.decisions.ts:72

All pre-migration contracts defaulted to 'iva'. Correction path needed.

medium

No PostgreSQL CHECK constraint on tax_regime

packages/database/drizzle/0058_material_kitty_pryde.sql:1

Add CHECK (tax_regime IN ('iva','iva_frontera')) to the migration to prevent NaN corruption from raw SQL.

low

Conditional spread obscures intent

domains/cross-domain/src/bill-for-contract.shells.ts:159

Replace ...(taxRegime ? { taxRegime } : {}) with taxRegime: taxRegime ?? 'iva' for clarity.

low

taxRegime not exposed in API response — clients cannot read it

apps/platform/src/api/mappers/utility-contract.mapper.ts:62

Correction flows from the UI are impossible if the field isn't in the response.

History · 6 commits

  1. a1f0898needs attentionincremental1H · 1M · 4L2026-07-13 20:37
  2. a72b534safeincremental0H · 0M · 0L2026-07-13 19:28
  3. 92e5afaneeds attentionincremental1H · 1M · 8L2026-07-13 16:47
  4. 7f40d56needs attentionincremental1H · 4M · 5L2026-07-13 16:38
  5. 5088e00needs attentionincremental3H · 4M · 4L2026-07-13 02:47
  6. 1bda877blockedfull12H · 12M · 9L2026-07-13 02:30current