The statement has been pinned to the calendar year in progress since bc74905.
Now that prior years are imported, the year becomes a choice: the current one
still reads the live ledger, and any earlier one reads that year's archive.
A period is selected by its `datos2@YYYY` tag, not by a date range. That is how
legacy addressed it — one table per closed year, `SELECT ... FROM `2025`` — and
the distinction is load-bearing: the archives carry rows dated a day or two into
the following January, so a date window would file them under the wrong year in
one direction and drop them in the other.
Two things the archive branch must not inherit:
- The balance floor. It exists to stop a later opening balance double-counting
the history it summarizes; for a period view that history is precisely what
is being asked for, so applying it would return nothing at all.
- The cash-source exclusion. It reproduces legacy's DATOS2-only datosfreak,
and an archive is DATOS2 rows already.
No fold into an opening balance either — the archive holds its own Jan-1 BALANCE
FORWARD row, which is the carry, listed exactly as legacy listed it.
The current period stays deliberately open-ended at the top. A period is a table
in legacy, not a date range, so whatever the office filed in it belongs to it,
including the future-dated rows the live ledger carries out to 2028. Bounding it
would hide them from every view.
`availableYears` reports the periods a customer actually has, so the picker never
offers a year that would render empty — "you had no activity in 2019" is a
different claim from "2019 was never imported", and only one of them is true.
The selector hides itself entirely for a customer with a single period, and a
year outside the list is a 404 rather than a silent fall back to the current one.
The same period rule lands on the printable twin (edo-cuenta-datos gains a
"Periodo (año)" parameter) and on the portal, where fetchLedgerRowsPlatform was
also filtering by date with no source exclusion at all — so period=2025 would
have returned the archive rows on top of that year's EFECTIVO receipts, counting
every prior-year payment twice. The portal's allowlist is now built per data
source and validated at the point of use: DreamHost holds the current year plus
one archive table, the platform holds however many were imported, and
fetchLedgerRowsLegacy interpolates the period as a table name, so a
platform-only year must not reach it — including on the fallback path when the
platform is unreachable.
Left alone: the /clientes/:id ledger card still shows the current year. It reads
transactionYear off the customers endpoint rather than the statement, it is a
summary that links to the full statement, and giving it its own year state would
duplicate the page it links to.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
37 lines
1.5 KiB
TypeScript
37 lines
1.5 KiB
TypeScript
import { periodSourceTable } from "./billing.service";
|
|
|
|
/**
|
|
* A closed year is imported as its own tagged set of rows rather than being
|
|
* identified by date. The tag is written by migration/transform_transactions.py
|
|
* and read by BillingService.statement, the edo-cuenta-datos report, and the
|
|
* PHP portal — three places that must agree on the exact string.
|
|
*/
|
|
describe("periodSourceTable", () => {
|
|
it("names the archive the migration writes", () => {
|
|
expect(periodSourceTable(2025)).toBe("datos2@2025");
|
|
expect(periodSourceTable(2024)).toBe("datos2@2024");
|
|
});
|
|
|
|
it("stays distinct from the live ledger's own table", () => {
|
|
// The live table is plain `datos2`. legacyId is a positional ordinal that
|
|
// restarts at 0 in every archive, so a shared name would collide with the
|
|
// current year row-for-row on the unique key.
|
|
expect(periodSourceTable(2025)).not.toBe("datos2");
|
|
expect(periodSourceTable(2025).startsWith("datos2@")).toBe(true);
|
|
});
|
|
|
|
it("is not matched by the statement's cash-source exclusion list", () => {
|
|
// STATEMENT_EXCLUDED_SOURCE_TABLES drops the EFECTIVO family to reproduce
|
|
// legacy's DATOS2-only datosfreak. An archive holds DATOS2 rows, so it must
|
|
// survive that filter or a prior year renders empty.
|
|
const excluded = [
|
|
"EFECTIVO",
|
|
"EFECTIVO_BACKUP",
|
|
"EFECTIVO FM3",
|
|
"CHEQUE FM3",
|
|
"IVA 2015",
|
|
];
|
|
expect(excluded).not.toContain(periodSourceTable(2025));
|
|
});
|
|
});
|