mega-plant/next.config.ts
David 5caa923375 Invoices: the app ingests PDFs itself, with a problem-invoice inbox
Extraction was previously manual: an agent read each PDF, mapped the columns
by hand and wrote data/source/invoice-*.json. That was the only way anything
got in, and the upload button just filed the document without reading it.

That supplier's report is one fixed template — the columns sit at identical
x-positions on every invoice, and only the line count and page count vary —
so the figures are now read by coordinate instead. Deliberately not an AI
extractor: this input does not need one, and a deterministic parse that fails
loudly is worth more here than a flexible one that can hallucinate a total.

lib/invoice-parser.ts reads the header, the machine lines across all pages,
the "Plant Hire Total" row and the SUB TOTAL / Escalation / PLUS VAT /
INVOICE TOTAL block, then applies the same arithmetic gates the seed does:
lines must sum to the subtotal, each line must satisfy qty x rate = amount
and amount + VAT = total, and VAT must match the stated rate. Anything that
does not reconcile is refused with the reason — never partially imported.

Validated by parsing all five existing invoices and comparing every field
against the hand-extracted JSON: identical, including the R24,205.13-VAT
column that a plain text read gets wrong. PHI 169 was then deleted and
re-ingested through the HTTP endpoint alone and came back field-for-field
identical, with no agent involved.

- "Add Invoice" drops a PDF in and imports it; lines are matched to plant
  items by the code the invoice prints, and an unmatched line (the
  container codes MPLANT04/MPLANT05) is left unassigned rather than guessed.
- A "Problem invoices" inbox lists every PDF that failed, with the reason,
  and Retry re-reads the stored document and re-runs the parse. Retry
  re-reports rather than claiming success, and only files imported data.
- invoice_intake collection records each attempt.

pdfjs-dist is in serverExternalPackages: bundled, it rewrites its own lazy
worker import to a .next path absent from the standalone image and every
parse dies with "Setting up fake worker failed". The parser also copies the
buffer before handing it over, since pdfjs transfers and detaches it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-10-02 14:30:17 +02:00

45 lines
2.2 KiB
TypeScript

import type { NextConfig } from "next";
import withPWAInit from "@ducanh2912/next-pwa";
const nextConfig: NextConfig = {
// No `env` block — NEXT_PUBLIC_ vars are picked up automatically at build time.
// Next.js blocks dev resources (HMR client, font optimizer, stack frames) for
// any origin that isn't localhost. When you open the dev server from another
// device on the LAN, the SSR HTML renders but /_next/hmr is refused, so the
// page never hydrates and every button/control is inert.
//
// Patterns are matched per dot-segment with `*` as a single-segment wildcard
// (same rules as `images.remotePatterns`) — CIDR ranges do NOT work here.
// Dev-only; `allowedDevOrigins` has no effect on `next build` / `next start`.
allowedDevOrigins: ["192.168.0.*", "100.88.155.*", "*.local"],
// Emits `.next/standalone/server.js` with a trimmed node_modules, so the
// runtime image doesn't need the full dependency tree. Required by the
// Dockerfile. Note it does NOT bundle `.next/static` or `public/` — the
// Dockerfile copies those in separately.
output: "standalone",
// pdfjs-dist dynamically imports its own worker module at runtime. When Next bundles
// it, that import is rewritten to a path inside .next that does not exist in the
// standalone image, and every parse dies with "Setting up fake worker failed".
// Keeping it external means it is required from node_modules at runtime and traced
// into the image, so the worker resolves next to the library that needs it.
serverExternalPackages: ["pdfjs-dist"],
};
const withPWA = withPWAInit({
dest: "public",
// Off in dev. next-pwa writes sw.js + workbox-*.js into `dest` on every
// compile, and the dev server watches public/ — so leaving it on makes each
// build trigger the next one, forever. A dev service worker also serves
// stale assets against HMR. Production builds are unaffected.
disable: process.env.NODE_ENV === "development",
cacheOnFrontEndNav: true,
aggressiveFrontEndNavCaching: true,
reloadOnOnline: true,
register: true,
workboxOptions: { cleanupOutdatedCaches: true, clientsClaim: true, skipWaiting: true },
});
export default withPWA(nextConfig);