← all branches

feat/billing-improve

needs attention
f406509 · fullpre-PRreviewed 2026-07-30 23:22 UTC3H · 7M · 5L · 9I
The branch
Purpose
Eliminate the redundant Tinybird read on bill-PDF generation and add a batch SFN pipeline for fleet-wide bill rendering.
Goal
Read-once bill compute: derive both charts from the same demand series. Shared mapper so both paths render identical PDFs.
Sub-goals
  • SG-1: deriveBillChartsFromSeries — pure function replacing resolveBillChartsShell
  • SG-2: bill-pdf-view-model.ts — shared ComputeBillResult→BillPdfViewModel mapper
  • SG-3: BillPdfGeneratorSfnStack — CDK SFN Map + per-bill Lambda
  • SG-4: generate-bill-pdf.handler.ts — Lambda batch worker (service-role)
  • SG-5: react-pdf parity (BAT-284): Poppins, stacked TOU bars, grey table, dashed demand lines
The changes (whole branch)
What
Deleted resolveBillChartsShell + platform bill-pdf.mapper.ts. Added pure deriveBillChartsFromSeries + shared bill-pdf-view-model.ts. Added Lambda + CDK SFN stack. Added 9-test suite. bill-document.tsx rewritten.
Why
Prior implementation made two Tinybird reads per bill. New reads once, enables SFN fan-out.
Areas
domains/cross-domain+33392packages/bill-pdf+260131infra/cdk+2012services/billing+1190apps/platform/src/api+23126
Blast
27 files, +1102/-351. cross-domain (bills), bill-pdf renderer, CDK billing stack, new billing Lambda, platform handler simplified.
tCO2e formula regression — fix before merge Prior review-fix pass applied (154af7f8, f4065096)
typecheck· node_modules not installedtests· node_modules not installedci· no open PR

Findings · 15

correctness3

high

tCO2e formula changed from kWh-based to MXN-based — inflates ~5×

domains/cross-domain/src/bill-pdf-view-model.ts:85

Old: consKwh × 0.000435 (correct, 0.435 tCO2e/MWh). New: subtotal × 0.000435 (wrong units). 10k kWh @ 50k MXN → old=4.35t, new=21.75t. Revert to kWh-based.

medium

TOU classify() silently drops points at schedule date gaps — bars∑≠bill kWh

domains/cross-domain/src/bill-charts-from-series.ts:111

if(band) skips unclassified points. Season-boundary schedule gap → bar undercount. Test fixture masks this with a full-year schedule.

low

SFN taskTimeout (3 min) > Lambda timeout (2 min) — unreachable dead code

infra/cdk/src/stacks/services/billing/bill-pdf-generator-sfn.stack.ts:120

security3

medium

SSRF: brand logo URLs passed to react-pdf Image without allowlist validation

services/billing/src/handlers/generate-bill-pdf.handler.ts:96

react-pdf fetches external URLs during render. Lambda has unrestricted outbound. Risk scoped to IAM holders who can invoke the SFN.

medium

LogLevel.ALL + includeExecutionData logs full bill payload to CloudWatch

infra/cdk/src/stacks/services/billing/bill-pdf-generator-sfn.stack.ts:157

sitePublicId, yearMonth, brand URLs written to CloudWatch. 1-week retention. Consider includeExecutionData: false.

low

Lambda event dates not validated for Invalid Date or from >= to

services/billing/src/handlers/generate-bill-pdf.handler.ts:61

conventions4

medium

cross-domain → @batu/bill-pdf new dependency edge — renderer coupling in domain layer

domains/cross-domain/package.json

No cycle (bill-pdf is a renderer leaf). Imports only from JSX-free subpath. Consider moving BillPdfViewModel to @batu/api/schemas.

low

SFN $.results heterogeneous — document the output contract

infra/cdk/src/stacks/services/billing/bill-pdf-generator-sfn.stack.ts

low

SiteMetricsWithSeries public barrel export — internal type leaks

domains/cross-domain/src/index.ts

low

.iterator() deprecated; consistent with existing convention; migrate both stacks together

infra/cdk/src/stacks/services/billing/bill-pdf-generator-sfn.stack.ts:143

tests5

high

computeBillShell({ includeSeries: true }) never tested — core feature unverified

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

The includeSeries flag and inputSeries propagation have no integration test. Pure derivation is well-tested; the pipeline glue is not.

high

toBillPdfViewModel / buildBillPdfRichMeta have no unit tests

domains/cross-domain/src/bill-pdf-view-model.ts

Shared mapper is SSOT for both bill-PDF paths. No test guards zone precedence, due-date, tCO2e formula, line sorting.

medium

TOU fixture identical day-types — selectScheduleForDate day-type selector untested

domains/cross-domain/src/__tests__/bill-charts-from-series.test.ts

medium

calendarMonthly guard branch untested

domains/cross-domain/src/bill-charts-from-series.ts

medium

Tijuana DST fall-back (Nov 1 2026) not tested — only spring-forward covered