feat(berths): normalize mooring numbers to canonical form

Sweep CRM mooring numbers from the legacy hyphen+padded form ("A-01")
to the canonical bare form ("A1") used by NocoDB, the public website,
the per-berth PDFs, and the Documenso EOI templates. Drift was
introduced by the original load-berths-to-port-nimara.ts seed; this
gates the Phase 3 public-website cutover where /berths/A1 URLs would
404 against a CRM still storing "A-01".

- 0024 data migration: idempotent regexp_replace + post-update sanity
  check that surfaces any non-conforming rows for manual triage.
- Invert normalizeLegacyMooring in dedup/migration-apply: it now
  canonicalizes ("D-32" -> "D32") instead of legacy-izing.
- Update tiptap-to-pdfme example tokens, EOI fixture moorings, and
  smoke-test seed moorings.
- Refresh seed-data/berths.json to canonical form; drop the now-
  redundant legacyMooringNumber field.
- Delete scripts/load-berths-to-port-nimara.ts (superseded in 0c).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Ciaccio
2026-05-05 01:59:26 +02:00
parent 8699f81879
commit 05be89ec6f
12 changed files with 10829 additions and 415 deletions

View File

@@ -62,7 +62,7 @@ function makeContext(overrides: Partial<EoiContext> = {}): EoiContext {
company: null,
owner: { type: 'client', name: 'Alice Smith' },
berth: {
mooringNumber: 'A-12',
mooringNumber: 'A12',
area: 'North',
lengthFt: '50',
price: '1000',
@@ -93,7 +93,7 @@ describe('fillEoiFormFields', () => {
expect(form.getTextField('Length').getText()).toBe('45');
expect(form.getTextField('Width').getText()).toBe('14');
expect(form.getTextField('Draft').getText()).toBe('6');
expect(form.getTextField('Berth Number').getText()).toBe('A-12');
expect(form.getTextField('Berth Number').getText()).toBe('A12');
expect(form.getCheckBox('Purchase').isChecked()).toBe(true);
expect(form.getCheckBox('Lease_10').isChecked()).toBe(false);