mega-plant/app/(dashboard)/expenses/suppliers-2/page.tsx
David 642d23dd24 Suppliers: replace the menu entry with the new page, flag over-invoiced POs, audited invoice amount corrections
- Menu: old Suppliers leaf hidden (route kept), Suppliers 2 renamed Suppliers
- PO invoice status: new 'over' state (never closed), live over-PO warning when typing
- Admin correction of invoice amount/date/number with mandatory reason, logged to purchase_invoice_edits
- Payable now carries unpaid balance forward month to month

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-29 20:49:05 +02:00

63 lines
3.2 KiB
TypeScript

import { Store } from "lucide-react";
import { serverAuth } from "@/lib/pb";
import { buildSupplierSpend } from "@/lib/supplier-spend";
import { Suppliers2View } from "@/components/Suppliers2View";
export const dynamic = "force-dynamic";
/**
* Suppliers 2 — the Income Overview treatment for suppliers: one card per company with a
* 12-month spend chart, this month's spend, and what is payable. Cash suppliers are hidden
* by default. Same data as /expenses/suppliers; nothing here is written.
*/
export default async function Suppliers2Page() {
const pb = await serverAuth();
const [suppliers, purchases, invoices, slips, payments] = await Promise.all([
pb.collection("suppliers").getFullList({ sort: "name" }),
pb.collection("purchases").getFullList({ fields: "id,po_no,po_date,total,supplier_ref" }),
pb.collection("purchase_invoices").getFullList({ fields: "purchase,invoice_no,invoice_date,amount_incl" }).catch(() => [] as any[]),
pb.collection("cash_float_entries").getFullList({ filter: 'kind = "expense"', fields: "amount,occurred_on,created,supplier_ref" }),
// Created by scripts/setup-supplier-payments-collection.mjs; absent on a fresh database.
pb.collection("supplier_payments").getFullList().catch(() => [] as any[]),
]);
const invByPo = new Map<string, any[]>();
for (const v of invoices) invByPo.set(v.purchase, [...(invByPo.get(v.purchase) ?? []), v]);
const now = new Date();
const rows = buildSupplierSpend({
year: now.getFullYear(),
month: now.getMonth() + 1,
suppliers: suppliers.map((s) => ({ id: s.id, name: String(s.name), code: String(s.code || ""), terms: String(s.terms || "") })),
purchases: purchases.map((p) => ({
id: p.id, po_no: String(p.po_no || ""), po_date: String(p.po_date || ""), total: Number(p.total || 0), supplier_ref: String(p.supplier_ref || ""),
invoices: (invByPo.get(p.id) ?? []).map((v) => ({
invoice_no: String(v.invoice_no || ""), invoice_date: String(v.invoice_date || ""),
amount_incl: typeof v.amount_incl === "number" && v.amount_incl > 0 ? v.amount_incl : null,
})),
})),
payments: payments.map((x) => ({
id: x.id, supplier_ref: String(x.supplier || ""), paid_on: String(x.paid_on || ""),
description: String(x.description || ""), amount: Number(x.amount || 0),
})),
slips: slips.map((s) => ({ amount: Number(s.amount || 0), at: String(s.occurred_on || s.created || ""), supplier_ref: String(s.supplier_ref || "") })),
});
return (
<div className="p-4 sm:p-8 max-w-[1400px]">
<Suppliers2View
rows={rows}
year={now.getFullYear()}
month={now.getMonth() + 1}
title={
<div className="flex items-center gap-3">
<div className="w-10 h-10 shrink-0 rounded-xl bg-gradient-to-br from-[#30638E]/15 to-[#00798C]/15 grid place-items-center text-[#30638E]"><Store className="w-5 h-5" /></div>
<div>
<h1 className="text-2xl font-semibold text-[#003D5B] dark:text-white">Suppliers</h1>
<p className="text-gray-500 dark:text-gray-400 text-sm">Spend and payables per supplier</p>
</div>
</div>
}
/>
</div>
);
}