From 9929a9a3ac4030ef08031e2e1ef12c959fac3f02 Mon Sep 17 00:00:00 2001 From: Ricardo Mancinas Date: Tue, 18 Aug 2026 20:21:41 -0700 Subject: [PATCH] feat(customers): the customer file's estado de cuenta follows the same year rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `/clientes/:id` carries its own "Estado de cuenta" card, fed by CustomersService.detail rather than by BillingService.statement, so the previous commit left it reading the old way: the last 100 movements of all time, newest first. Two pages, one label, two orders. It now covers the current calendar year oldest-first, like the statement and like the sheet the office prints. The `take: 100` is gone with it — capping a descending list hid the oldest rows, but capping an ascending one would hide the newest, and a single year is small (365 rows for the heaviest customer in the book). The per-domain totals above the list are untouched and still historical: they are an unfloored groupBy, so they double-count every customer's opening balance. That is a separate bug from this one; the card now says out loud that those figures are lifetime, not this year's. Co-Authored-By: Claude Opus 5 --- apps/api/src/customers/customers.service.ts | 13 +++++++++++-- apps/web/src/app/clientes/[id]/page.tsx | 16 +++++++++------- apps/web/src/lib/types.ts | 2 ++ 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/apps/api/src/customers/customers.service.ts b/apps/api/src/customers/customers.service.ts index b523737..22af4b3 100644 --- a/apps/api/src/customers/customers.service.ts +++ b/apps/api/src/customers/customers.service.ts @@ -96,6 +96,13 @@ export class CustomersService { /** Full unified customer view: identity + both business lines + ledger. */ async detail(id: string) { + // The movement list on the customer file is the same statement the office + // prints, so it follows the same rule as BillingService.statement: this + // calendar year, oldest-first. No `take` any more — the cap used to hide + // the end of a busy customer's year once the order flipped, and a single + // year is small (365 rows for the heaviest customer in the book). + const yearStart = new Date(Date.UTC(new Date().getUTCFullYear(), 0, 1)); + const customer = await this.prisma.customer.findUnique({ where: { id }, include: { @@ -117,8 +124,8 @@ export class CustomersService { }, }, transactions: { - orderBy: { transactionDate: "desc" }, - take: 100, + where: { transactionDate: { gte: yearStart } }, + orderBy: [{ transactionDate: "asc" }, { id: "asc" }], include: { type: true }, }, }, @@ -140,6 +147,8 @@ export class CustomersService { return { ...customer, + /** Calendar year the movement list covers. */ + transactionYear: yearStart.getUTCFullYear(), transactionSummary: summary.map((s) => ({ domain: s.domain, currency: s.currency, diff --git a/apps/web/src/app/clientes/[id]/page.tsx b/apps/web/src/app/clientes/[id]/page.tsx index df8e249..8d7c3dd 100644 --- a/apps/web/src/app/clientes/[id]/page.tsx +++ b/apps/web/src/app/clientes/[id]/page.tsx @@ -128,6 +128,7 @@ function Detail({ id }: { id: string }) { customerId={data.id} summary={data.transactionSummary} transactions={data.transactions} + year={data.transactionYear} /> @@ -746,16 +747,18 @@ function EstadoCuentaSection({ customerId, summary, transactions, + year, }: { customerId: string; summary: TransactionSummaryRow[]; transactions: Transaction[]; + year: number; }) { return (
@@ -782,7 +785,7 @@ function EstadoCuentaSection({
{transactions.length === 0 ? ( -
Sin movimientos registrados.
+
Sin movimientos en {year}.
) : (
@@ -804,11 +807,10 @@ function EstadoCuentaSection({
)} - {transactions.length >= 100 && ( -
- Mostrando los 100 movimientos más recientes. -
- )} +
+ Movimientos de {year}, del más antiguo al más reciente. Los saldos de + arriba son el acumulado histórico, no el del año. +
{transactions.length > 0 && (

diff --git a/apps/web/src/lib/types.ts b/apps/web/src/lib/types.ts index c694702..e9f0b67 100644 --- a/apps/web/src/lib/types.ts +++ b/apps/web/src/lib/types.ts @@ -1129,6 +1129,8 @@ export interface CustomerDetail { properties: Property[]; policies: Policy[]; transactions: Transaction[]; + /** Calendar year `transactions` covers. */ + transactionYear: number; transactionSummary: TransactionSummaryRow[]; }