invoice.reconstruct-from-coefficients — explanation
Goal
Reproduce a supplier's invoice math from two index reads (start, end) using
the regulatory profile coefficients published by Enedis. Cross-check our
ranker so the optimization we surface matches what the customer will actually
pay.
Inputs
```
{
offer: TariffOffer,
// Half-hourly kWh series for the billing period. If unavailable, the
// caller reconstructs it from index reads × normalized profile
// coefficients (which sum to 1 over the period).
halfHourSeries: number[],
// Same-length boolean mask — required for HP/HC offers, ignored otherwise.
hpMask?: boolean[],
// Days in the billing period — used to prorate the annual abonnement.
billingDays: number,
// Optional supplemental charges
turpeEur?: number,
taxesEur?: number,
}
```
Algorithm
priceHalfHourSeries(offer, halfHourSeries, hpMask)
from lib/tariff/price-halfhour.ts. Same function the ranker calls.
offer.abonnement_annual_eur × billingDays / 365.
from lib/methodology/methods/turpe/).
same offer + same total kWh, minus our (energy + abo) reconstruction
annualized. Used by tests to guard the two paths.
Why a separate method?
The ranker works at year-aggregate granularity (annual kWh + estimated HP
fraction). This method works at half-hour granularity. Both share
priceHalfHourSeries / priceAggregateEnergy from
lib/tariff/price-halfhour.ts — different inputs, same arithmetic.
Golden bills
Three anonymized bills under ./__fixtures__/golden-{1,2,3}/ ship with the
method. Each fixture provides the half-hour series, the offer, the billing
period, and the observed invoice total. The test asserts the reconstructed
total is within €1 of the observed total and that
|delta_vs_ranker_eur| ≤ 1 on the annualized view.