fix(reports/legacy): freshness from service date, not invoice date

Marc Robidoux flagged as overpriced (129.95$) — he has a loyalty discount
(service id 74448) that should lower it. Investigation: 74448 doesn't
exist in the copy (its max service id is 74393), so the discount was added
after the snapshot. Same freshness issue as Julie Dupuis — not a calc bug.

But this also exposed that the freshness banner was wrong: it read the
newest INVOICE date (Apr 30) while the snapshot actually carries SERVICES
created through May 22 — May's recurring billing run simply hadn't executed
at dump time, so invoices lag services by ~3 weeks. For a report that reads
active services/plans/discounts, the service date is the right freshness
signal.

fetchDataAsOf now returns both {services, invoices}; data_as_of (shown in
the banner) is the service date (May 22), with last_invoice kept for
reference. The copy is ~10 days stale, not ~1 month. Marc's loyalty credit
still won't show until the copy is refreshed (task #38).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
louispaulb 2026-06-01 19:57:09 -04:00
parent 7413743572
commit ab57a3e135

View File

@ -176,17 +176,29 @@ function buildQuery (url) {
return { sql, params, meta: { threshold, segment, includeAddons, cats, limit } }
}
// Freshness probe — the most recent invoice date in the legacy copy tells
// us how stale the data is. The copy is a one-shot snapshot (no auto-sync),
// so the report must advertise its as-of date to avoid misleading the
// operator into acting on month-old prices (a re-negotiated client like
// Julie Dupuis won't show their new discount until the copy is refreshed).
// Freshness probe — the copy is a one-shot snapshot (no auto-sync), so the
// report advertises its as-of date so nobody acts on stale prices.
//
// This report reads SERVICES (active plans/discounts), not invoices, so the
// relevant freshness is the newest service.date_orig — NOT the newest
// invoice date. They differ: the snapshot carries services created up to
// ~May 22 but invoices only through Apr 30 (May's recurring billing run
// hadn't happened at dump time). Using the invoice date understated
// freshness by ~3 weeks. We return both; the UI shows the service one.
// (A discount added after the snapshot — e.g. Marc Robidoux's loyalty
// credit, service id 74448 > the copy's max 74393 — still won't appear
// until the copy is refreshed.)
async function fetchDataAsOf (pool) {
const out = { services: null, invoices: null }
try {
const [r] = await pool.execute('SELECT MAX(date_orig) AS max_ts FROM invoice')
const ts = r?.[0]?.max_ts
return ts ? new Date(ts * 1000).toISOString() : null
} catch { return null }
const [s] = await pool.execute('SELECT MAX(date_orig) AS max_ts FROM service')
if (s?.[0]?.max_ts) out.services = new Date(s[0].max_ts * 1000).toISOString()
} catch { /* ignore */ }
try {
const [i] = await pool.execute('SELECT MAX(date_orig) AS max_ts FROM invoice')
if (i?.[0]?.max_ts) out.invoices = new Date(i[0].max_ts * 1000).toISOString()
} catch { /* ignore */ }
return out
}
async function handleJson (req, res, url) {
@ -196,12 +208,15 @@ async function handleJson (req, res, url) {
try {
const t0 = Date.now()
const [rows] = await pool.execute(sql, params)
const dataAsOf = await fetchDataAsOf(pool)
const asOf = await fetchDataAsOf(pool)
log(`legacy report overpriced-internet: ${rows.length} rows in ${Date.now() - t0}ms (threshold=${meta.threshold}, segment=${meta.segment}, addons=${meta.includeAddons})`)
return json(res, 200, {
threshold: meta.threshold, segment: meta.segment,
include_addons: meta.includeAddons, categories: meta.cats,
data_as_of: dataAsOf,
// data_as_of = service freshness (the relevant one for this report);
// last_invoice kept for reference.
data_as_of: asOf.services,
last_invoice: asOf.invoices,
count: rows.length, rows,
})
} catch (e) {