feat(statements): scope the estado de cuenta to the current year, oldest-first
The office's EDO CUENTA sheet has always been a *year* statement: a balance
forward line dated January 1st, then that year's movements in the order they
happened. Both of ours read the other way — every year the customer ever had,
newest first — so staff comparing the screen against the printed sheet were
reading two different documents.
Movements are now bounded to the calendar year and returned ascending, on the
screen (/estado-cuenta/[id]) and in the printable `edo-cuenta-datos` report
alike.
Earlier rows are dropped from the *list*, not from the arithmetic. The balance
floor normally lands on January 1st already, so for most customers nothing
extra is dropped at all; when it doesn't — a customer the last legacy publish
skipped, or one that never had an opening balance — the earlier rows are
folded into a carried balance and shown as a single "saldo anterior" line.
Discarding them instead would restart every balance at zero on January 1st and
nothing would throw; the numbers would just be wrong, which is how the
double-counting bug survived for years. `opening` is exposed per currency and
per business line so the totals still reconcile against the last running
balance printed.
Two things the report was missing on its own are fixed while it is being
touched, since it must agree with the screen to the peso:
- it never applied the balance floor, so every pre-cutover row was counted
twice — once inside the opening balance and once as itself;
- its source-table exclusion used a bare `notIn`, and `NULL NOT IN (...)` is
NULL rather than true, so every app-captured row (which has no
legacySourceTable) silently vanished from the printout.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -36,10 +36,12 @@ import type {
|
||||
* charge and an insurance payment finally sit on the same page, under the same
|
||||
* person, with a running balance.
|
||||
*
|
||||
* The running balance is per currency (the API accumulates it chronologically
|
||||
* before handing the list back newest-first), so the movement table is scoped
|
||||
* to one currency at a time — a column that alternated between pesos and
|
||||
* dollars would be a meaningless number.
|
||||
* The running balance is per currency, so the movement table is scoped to one
|
||||
* currency at a time — a column that alternated between pesos and dollars would
|
||||
* be a meaningless number.
|
||||
*
|
||||
* Like the legacy EDO CUENTA report, the table covers the current year only and
|
||||
* runs oldest-first, opening on the balance carried in from before it.
|
||||
*/
|
||||
export default function EstadoCuentaDetailPage({
|
||||
params,
|
||||
@@ -194,7 +196,7 @@ function StatementView({ id }: { id: string }) {
|
||||
<section className="section">
|
||||
<SectionHead
|
||||
rule="cuenta"
|
||||
title="Movimientos"
|
||||
title={`Movimientos ${data.year}`}
|
||||
count={movements.length}
|
||||
countSuffix={movements.length === 1 ? "movimiento" : "movimientos"}
|
||||
right={
|
||||
@@ -260,7 +262,7 @@ function StatementView({ id }: { id: string }) {
|
||||
<div className="card">
|
||||
{movements.length === 0 ? (
|
||||
<div className="empty-inline">
|
||||
Sin movimientos en {currency}
|
||||
Sin movimientos de {data.year} en {currency}
|
||||
{domain ? ` para ${domainLabel(domain)}` : ""}.
|
||||
</div>
|
||||
) : (
|
||||
@@ -282,6 +284,26 @@ function StatementView({ id }: { id: string }) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{/*
|
||||
The carried balance, shown the way the legacy report shows
|
||||
it: a BALANCE FORWARD line above the year's movements. It
|
||||
only appears when there is something to carry — when the
|
||||
customer's opening-balance row is itself dated inside this
|
||||
year (the usual case) it is listed as an ordinary movement
|
||||
and this row is zero, so it is left out.
|
||||
|
||||
Suppressed under a business-line filter: the carried balance
|
||||
is the customer's, across both lines, and printing it above
|
||||
one line's rows would read as that line's opening balance.
|
||||
*/}
|
||||
{!domain && Number(active?.opening ?? 0) !== 0 && (
|
||||
<OpeningRow
|
||||
opening={active!.opening}
|
||||
currency={currency}
|
||||
year={data.year}
|
||||
canVoid={canVoid}
|
||||
/>
|
||||
)}
|
||||
{movements.map((m) => (
|
||||
<StatementRow
|
||||
key={m.id}
|
||||
@@ -481,6 +503,44 @@ function ConceptosSection({
|
||||
);
|
||||
}
|
||||
|
||||
/** The balance carried into the statement year — legacy's BALANCE FORWARD. */
|
||||
function OpeningRow({
|
||||
opening,
|
||||
currency,
|
||||
year,
|
||||
canVoid,
|
||||
}: {
|
||||
opening: string;
|
||||
currency: LedgerCurrency;
|
||||
year: number;
|
||||
canVoid: boolean;
|
||||
}) {
|
||||
return (
|
||||
<tr>
|
||||
<td className="mono" style={{ whiteSpace: "nowrap" }}>
|
||||
{formatDate(`${year}-01-01T00:00:00.000Z`)}
|
||||
</td>
|
||||
<td className="tx-domain-cell">Ambas líneas</td>
|
||||
<td>
|
||||
Saldo anterior
|
||||
<div className="tx-concept">Al cierre de {year - 1}</div>
|
||||
</td>
|
||||
<td className="tx-ref">—</td>
|
||||
<td className="num">
|
||||
<span className={`tx-amount ${Number(opening) < 0 ? "neg" : "pos"}`}>
|
||||
{formatMoney(opening, currency)}
|
||||
</span>
|
||||
</td>
|
||||
<td className="num">
|
||||
<span className={`bal-running ${balanceTone(opening)}`}>
|
||||
{formatMoney(opening, currency)}
|
||||
</span>
|
||||
</td>
|
||||
{canVoid && <td />}
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
function StatementRow({
|
||||
m,
|
||||
canVoid,
|
||||
|
||||
@@ -1041,6 +1041,8 @@ export interface BillingFacets {
|
||||
|
||||
export interface StatementSummary {
|
||||
currency: LedgerCurrency;
|
||||
/** Balance carried in from before the statement year — legacy's BALANCE FORWARD. */
|
||||
opening: string;
|
||||
charges: string;
|
||||
credits: string;
|
||||
balance: string;
|
||||
@@ -1054,6 +1056,7 @@ export interface StatementSummary {
|
||||
export interface StatementDomainRow {
|
||||
domain: TransactionDomain;
|
||||
currency: LedgerCurrency;
|
||||
opening: string;
|
||||
charges: string;
|
||||
credits: string;
|
||||
balance: string;
|
||||
@@ -1089,6 +1092,8 @@ export interface Statement {
|
||||
propertyCount: number;
|
||||
policyCount: number;
|
||||
};
|
||||
/** Calendar year the statement covers; movements are scoped to it. */
|
||||
year: number;
|
||||
summary: StatementSummary[];
|
||||
byDomain: StatementDomainRow[];
|
||||
byType: StatementTypeRow[];
|
||||
|
||||
Reference in New Issue
Block a user