Keep a site's invoice table consistent with its own totals

buildSiteSummaries counts only internal invoice lines towards a site, but this
page built its line-item table from every line whose invoice named the site. On a
site carrying both, the table would have listed lines the totals excluded — the
two halves of one page disagreeing.

Both now filter on the internal party, so the table and the totals are always the
same set. A site left with no internal invoices has no summary and notFound()
fires, which is correct: "Move containers" now returns a 404 rather than being
presented as a yard.
This commit is contained in:
David 2026-10-08 12:07:07 +02:00
parent 80ca4788a2
commit 1b763ae99d

View file

@ -4,6 +4,7 @@ import { notFound } from "next/navigation";
import { getAssets, getInvoices, getInvoiceLines, getSites, joinBookedLines, MONTH_NAMES } from "@/lib/server-data";
import { buildSiteSummaries } from "@/lib/sites";
import { INTERNAL_INVOICES_PATH, EXTERNAL_INVOICES_PATH } from "@/lib/invoice-routes";
import { isInternalParty } from "@/lib/invoice-party";
import { ZAR2 } from "@/lib/utils";
import { ScrollFade } from "@/components/ScrollFade";
import { ArrowLeft, MapPin, FileText } from "lucide-react";
@ -38,8 +39,13 @@ export default async function SiteDetailPage({ params }: { params: Promise<{ id:
const invById = new Map(invoices.map((i) => [i.id, i]));
const assetById = new Map(assets.map((a) => [a.id, a]));
// Internal invoices only, exactly as buildSiteSummaries counts them. Without this the
// table below would list lines the site's totals exclude — the two must not disagree.
const siteLines = joined
.filter((l) => invById.get(l.invoice)?.site === site.name)
.filter((l) => {
const inv = invById.get(l.invoice);
return inv?.site === site.name && isInternalParty(inv);
})
.sort((a, b) => (invById.get(b.invoice)!.invoice_date).localeCompare(invById.get(a.invoice)!.invoice_date) || a.line_no - b.line_no);
return (