2026-09-18 17:51:37 +00:00
|
|
|
import Link from "next/link";
|
feat: Fedex import flow, orders + invoice APIs, payment bundles
- /fedex: batch create, document upload/replace/delete, save shipment, imported list
- /api/orders, /api/invoices, /api/pb-file, /api/pb/refresh-token, /api/stock-codes
- /api/payments/bundle: generate + download-all remittance bundles
- shared app/components/DocCell.tsx and app/lib/{invoice-docs,pb-fetch,pb-token}.ts
- recon: invoice reconciliation reports and PowerShell fix-plan scripts
- .gitignore: exclude one-off scripts/_* debugging artefacts, recon/pdfs+txts, data backups
2026-10-18 15:27:40 +00:00
|
|
|
import { getPb, fmtGBP, fmtDate, pbFileUrl, invoiceFileName } from "../lib/pb";
|
2026-09-18 17:51:37 +00:00
|
|
|
import RemittanceClient from "./RemittanceClient";
|
|
|
|
|
|
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
|
|
|
|
|
|
const PB_URL = process.env.NEXT_PUBLIC_PB_URL || process.env.PB_URL || "https://pb.agent.apphub.co.za";
|
|
|
|
|
|
|
|
|
|
async function fetchBatchWithInvoices(pb: any, batchId: string) {
|
|
|
|
|
const inv = await pb.collection("JEI_invoices").getList(1, 500, {
|
|
|
|
|
filter: `payment_batch="${batchId}"`,
|
|
|
|
|
sort: "-invoice_date",
|
feat: Fedex import flow, orders + invoice APIs, payment bundles
- /fedex: batch create, document upload/replace/delete, save shipment, imported list
- /api/orders, /api/invoices, /api/pb-file, /api/pb/refresh-token, /api/stock-codes
- /api/payments/bundle: generate + download-all remittance bundles
- shared app/components/DocCell.tsx and app/lib/{invoice-docs,pb-fetch,pb-token}.ts
- recon: invoice reconciliation reports and PowerShell fix-plan scripts
- .gitignore: exclude one-off scripts/_* debugging artefacts, recon/pdfs+txts, data backups
2026-10-18 15:27:40 +00:00
|
|
|
fields: "id,invoice_no,invoice_date,buyer_name_variant,total_net,carriage_net,invoice_total,pdf_file",
|
2026-09-18 17:51:37 +00:00
|
|
|
});
|
|
|
|
|
return inv.items as any[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default async function RemittancePage() {
|
|
|
|
|
const pb = getPb();
|
|
|
|
|
let batches: any[] = [];
|
|
|
|
|
let errorMsg: string | null = null;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const r = await pb.collection("JEI_payment_batches").getList(1, 500, {
|
2026-09-18 17:54:08 +00:00
|
|
|
sort: "-payment_date",
|
feat: Fedex import flow, orders + invoice APIs, payment bundles
- /fedex: batch create, document upload/replace/delete, save shipment, imported list
- /api/orders, /api/invoices, /api/pb-file, /api/pb/refresh-token, /api/stock-codes
- /api/payments/bundle: generate + download-all remittance bundles
- shared app/components/DocCell.tsx and app/lib/{invoice-docs,pb-fetch,pb-token}.ts
- recon: invoice reconciliation reports and PowerShell fix-plan scripts
- .gitignore: exclude one-off scripts/_* debugging artefacts, recon/pdfs+txts, data backups
2026-10-18 15:27:40 +00:00
|
|
|
fields: "id,batch_ref,payment_date,pop_file,zar_paid,exchange_rate,notes,bundled_invoices_pdf,bundled_waybills_pdf,bundled_sad500_pdf,bundled_sad507_pdf,bundled_bill_of_entry_pdf,bundled_edi_pdf",
|
2026-09-18 17:51:37 +00:00
|
|
|
});
|
|
|
|
|
batches = r.items;
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
errorMsg = String(e?.message || e);
|
|
|
|
|
}
|
|
|
|
|
|
feat: Fedex import flow, orders + invoice APIs, payment bundles
- /fedex: batch create, document upload/replace/delete, save shipment, imported list
- /api/orders, /api/invoices, /api/pb-file, /api/pb/refresh-token, /api/stock-codes
- /api/payments/bundle: generate + download-all remittance bundles
- shared app/components/DocCell.tsx and app/lib/{invoice-docs,pb-fetch,pb-token}.ts
- recon: invoice reconciliation reports and PowerShell fix-plan scripts
- .gitignore: exclude one-off scripts/_* debugging artefacts, recon/pdfs+txts, data backups
2026-10-18 15:27:40 +00:00
|
|
|
// Fetch invoices per batch sequentially (NOT Promise.all — PB JS SDK auto-cancels
|
|
|
|
|
// parallel requests on the same client). Each batch triggers its own PB round-trip.
|
|
|
|
|
let batchWithInvoices: { batch: any; invoices: any[]; popUrl: string | null; bundleUrls: any }[] = [];
|
2026-09-18 17:51:37 +00:00
|
|
|
if (!errorMsg) {
|
feat: Fedex import flow, orders + invoice APIs, payment bundles
- /fedex: batch create, document upload/replace/delete, save shipment, imported list
- /api/orders, /api/invoices, /api/pb-file, /api/pb/refresh-token, /api/stock-codes
- /api/payments/bundle: generate + download-all remittance bundles
- shared app/components/DocCell.tsx and app/lib/{invoice-docs,pb-fetch,pb-token}.ts
- recon: invoice reconciliation reports and PowerShell fix-plan scripts
- .gitignore: exclude one-off scripts/_* debugging artefacts, recon/pdfs+txts, data backups
2026-10-18 15:27:40 +00:00
|
|
|
for (const b of batches) {
|
|
|
|
|
try {
|
|
|
|
|
const invoices = await fetchBatchWithInvoices(pb, b.id);
|
|
|
|
|
// Add a per-invoice pdfUrl so "View invoice →" opens the PDF directly
|
|
|
|
|
const invoicesWithPdf = invoices.map((inv) => ({
|
|
|
|
|
...inv,
|
|
|
|
|
pdfUrl: inv.pdf_file
|
|
|
|
|
? pbFileUrl("JEI_invoices", inv.id, "pdf_file", invoiceFileName(inv.invoice_no, inv.id))
|
|
|
|
|
: null,
|
|
|
|
|
}));
|
|
|
|
|
const batchTag = String(b.batch_ref || b.id);
|
|
|
|
|
const popUrl = b.pop_file
|
|
|
|
|
? pbFileUrl("JEI_payment_batches", b.id, "pop_file", `${batchTag}-proof-of-payment`)
|
|
|
|
|
: null;
|
|
|
|
|
// Build bundle URLs for each of the 6 categories if the file is present
|
|
|
|
|
const mkUrl = (f: string | undefined, field: string, tag: string) =>
|
|
|
|
|
f ? pbFileUrl("JEI_payment_batches", b.id, field, `${batchTag}-${tag}`, true) : undefined;
|
|
|
|
|
const bundleUrls = {
|
|
|
|
|
invoice: mkUrl(b.bundled_invoices_pdf, "bundled_invoices_pdf", "invoices"),
|
|
|
|
|
waybill: mkUrl(b.bundled_waybills_pdf, "bundled_waybills_pdf", "waybills"),
|
|
|
|
|
sad500: mkUrl(b.bundled_sad500_pdf, "bundled_sad500_pdf", "sad500"),
|
|
|
|
|
sad507: mkUrl(b.bundled_sad507_pdf, "bundled_sad507_pdf", "sad507"),
|
|
|
|
|
bill_of_entry: mkUrl(b.bundled_bill_of_entry_pdf, "bundled_bill_of_entry_pdf", "bill-of-entry"),
|
|
|
|
|
edi: mkUrl(b.bundled_edi_pdf, "bundled_edi_pdf", "edi"),
|
|
|
|
|
};
|
|
|
|
|
batchWithInvoices.push({ batch: b, invoices: invoicesWithPdf, popUrl, bundleUrls });
|
|
|
|
|
} catch (_) {
|
|
|
|
|
batchWithInvoices.push({ batch: b, invoices: [], popUrl: null, bundleUrls: {} });
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-09-18 17:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<h1 className="text-2xl font-bold text-brand-dark">Remittance</h1>
|
|
|
|
|
<div className="text-sm text-slate-500">{batches.length} batch(es)</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{errorMsg && (
|
|
|
|
|
<div className="bg-red-50 border border-red-200 text-red-800 rounded p-4">
|
|
|
|
|
<div className="font-semibold">Cannot reach PocketBase</div>
|
|
|
|
|
<div className="text-sm mt-1">{errorMsg}</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{!errorMsg && batches.length === 0 && (
|
|
|
|
|
<div className="bg-white rounded-lg shadow p-8 text-center text-slate-500">
|
|
|
|
|
No payment batches yet. Tick invoices on the{" "}
|
|
|
|
|
<Link href="/unpaid" className="text-brand hover:underline">Unpaid tab</Link>{" "}
|
|
|
|
|
and click <strong>Create Payment</strong>.
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{!errorMsg && batches.length > 0 && (
|
|
|
|
|
<RemittanceClient batches={batchWithInvoices} />
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|