Files
pn-new-crm/assets
Matt eab30c194a fix(audit-wave-9): PDF correctness + brand asset hardening (pdf-auditor)
Address the pdf-auditor findings that survived the 2026-05-12 PDF stack
overhaul (pdfme → react-pdf). Items C-2/C-3 (tiptap-to-pdfme bugs) were
resolved when that 571-LOC bridge was deleted; remaining items:

- **M-7 wrong-port brand fallback** — replace `'Port Nimara'` defaults
  in PDF-rendering services. `reports.service` and `expense-export`
  throw when the port row is missing (the job is FK-keyed on a real
  port, so absence = broken state, must not stamp a competitor brand).
  `record-export` uses `'(port)'` as the visible placeholder.

- **M-2 silent field drift in fill-eoi-form** — promote the
  always-silent catch in `setText` / `setCheckbox` to log a structured
  warning per missing field (mirroring the existing `setBerthRange`
  pattern). A re-cut template with drifted AcroForm field names now
  surfaces in ops logs instead of shipping with empty values.

- **M-3 form not flattened** — `fillEoiFormFields` now flattens the
  AcroForm before save. Documenso pathway flattens server-side; this
  brings the in-app pathway to parity, so the signer can't edit
  pre-filled yacht dimensions / address / berth number after the fact.

- **M-1 PDF metadata** — set Title / Author / Subject / Lang / Producer
  / Creator on the generated EOI PDF for downstream readers and a11y
  tooling.

- **M-4 noisy berth-range warnings** — downgrade per-mooring warn to
  debug; emit a single summary warn per call when any passthrough
  occurred. Multi-berth EOIs with archived/legacy moorings no longer
  spam the log on every render.

- **M-6 source PDF sha pinning** — pin
  `assets/eoi-template.pdf` sha256 via `EXPECTED_EOI_SHA256` (exported
  for tests); `loadEoiTemplatePdf` warns once per process when the
  bytes drift without an explicit hash bump. Documented the
  intentional-update workflow in `assets/README.md`.

Tests updated in `tests/unit/pdf/fill-eoi-form.test.ts` to reflect
flatten + metadata (form fields are gone after flatten; pdf-lib has no
getLanguage so we assert the other setters round-trip).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:07:57 +02:00
..

assets/

Server-side runtime assets bundled by Next.js (via outputFileTracingIncludes in next.config.ts). These files are read with fs.readFile from process.cwd() at runtime, so they are NOT served as public URLs — use public/ for that.

eoi-template.pdf

The source PDF used by the in-app EOI generation pathway (src/lib/pdf/fill-eoi-form.ts). It must be the same PDF that the Documenso EOI template uploads, so both pathways produce equivalent documents.

The PDF must contain AcroForm fields with these exact names (mirroring the Documenso template's formValues keys — see docs/eoi-documenso-field-mapping.md):

Field name Type Filled with
Name Text EoiContext.client.fullName
Email Text EoiContext.client.primaryEmail
Address Text street, city, country
Yacht Name Text EoiContext.yacht.name
Length Text EoiContext.yacht.lengthFt
Width Text EoiContext.yacht.widthFt
Draft Text EoiContext.yacht.draftFt
Berth Number Text EoiContext.berth.mooringNumber
Lease_10 Checkbox always false (legacy default — Purchase, not Lease)
Purchase Checkbox always true

The fill path flattens the AcroForm after writing values, so the recipient can't edit pre-filled values (yacht dimensions, address, berth number) after the fact. Documenso pathway flattens server-side; the in-app pathway brings the artifact to parity.

Expected sha256

The source PDF's sha256 is pinned to guard against silent template swaps (an unreviewed asset swap would change legal output without a code diff):

ba495fd88d99ebe4b7f61acbe397fb2f1cd116e1e1f1b217de93106915c7c44b

scripts/check-eoi-template-sha.ts verifies this at boot of the in-app pathway; the function exposes the expected hash via EXPECTED_EOI_SHA256 so tests can re-check after a deliberate template revision.

To intentionally update the template:

  1. Drop the new PDF as eoi-template.pdf.
  2. Run shasum -a 256 assets/eoi-template.pdf.
  3. Update the hash in this README and in src/lib/pdf/fill-eoi-form.ts (search for EXPECTED_EOI_SHA256).

Override path

In dev/test, set EOI_TEMPLATE_PDF_PATH=/abs/path/to/your/template.pdf to point at a different file (e.g. a fixture).

How to extract this PDF

The legacy flow uploads this PDF to Documenso template ID 8. To get the exact bytes:

  1. In Documenso, open the EOI template.
  2. Download the source PDF.
  3. Drop it here as eoi-template.pdf.