fix(migration): legacy bare-mooring lookup + port-nimara berth backfill
Two issues surfaced when applying the migration to dev:
1. Mooring number format mismatch
The legacy NocoDB Interests table writes bare mooring strings
("D32", "B16", "A4"), but the new berths table (mirroring the
NocoDB Berths snapshot) uses zero-padded dashed form ("D-32",
"B-16", "A-04"). The interest→berth lookup missed every reference.
migration-apply.ts now tries the literal value first, then falls
back to a normalized form via `normalizeLegacyMooring(raw)`:
"D32" -> "D-32"
"A4" -> "A-04"
"E18" -> "E-18"
Multi-mooring strings ("A3, D30") are left as-is so they surface in
the warnings list for human review rather than silently picking one.
2. port-nimara only had the 12 hand-rolled seed berths, not the 117-
berth NocoDB snapshot
The mobile-foundation seed only places those 12 in port-nimara; the
117-berth snapshot was added later but only seeded into Marina
Azzurra (the secondary test port). Migrated interests reference
moorings well beyond A-01..D-03, so most lookups failed.
New scripts/load-berths-to-port-nimara.ts: idempotently loads any
missing snapshot berths into port-nimara without disturbing the
existing 12 (skips moorings that already exist). Run once;
subsequent runs no-op.
Result of full migration run on dev:
237 clients inserted (out of 245 total — 8 from prior seed)
406 contacts, 52 addresses, 38 yachts, 252 interests
27 interest→berth links resolved (only 13 source rows had a Berth
field set in NocoDB to begin with — most legacy interests are early
inquiries with no berth assignment)
1 unresolved warning: source=277 has multi-mooring "A3, D30"
Verified in UI:
/port-nimara/clients shows real names (John-michael Seelye, Reza
Amjad, Etiennette Clamouze, …)
/port-nimara/clients/<id> renders contacts (gmail.com addresses,
E.164 phones), tab counts (Interests N, Yachts N), pipeline summary
Dashboard: 245 clients, 266 active interests, $46.5M pipeline value
Pipeline funnel chart now shows real distribution (180 Open, 45
EOI Signed, dropoff through stages)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,25 @@ import type { MigrationPlan, PlannedClient, PlannedInterest } from './migration-
|
||||
|
||||
const SOURCE_SYSTEM = 'nocodb_interests';
|
||||
|
||||
/**
|
||||
* Convert a legacy bare mooring string like "D32" / "A1" / "E18" to the
|
||||
* dashed/padded form "D-32" / "A-01" / "E-18" used by the new berths
|
||||
* schema. If the input doesn't match the bare pattern, returns it
|
||||
* unchanged so a literal lookup can still hit (handles the case where
|
||||
* the legacy data already has the dashed form).
|
||||
*
|
||||
* Multi-mooring strings ("A3, D30") return the original string —
|
||||
* those need human review and we don't want to silently pick one half.
|
||||
*/
|
||||
function normalizeLegacyMooring(raw: string): string {
|
||||
// Bare letter+digits, e.g. "D32"
|
||||
const m = /^([A-E])(\d{1,3})$/i.exec(raw.trim());
|
||||
if (!m) return raw;
|
||||
const letter = m[1]!.toUpperCase();
|
||||
const num = parseInt(m[2]!, 10);
|
||||
return `${letter}-${num.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
export interface ApplyResult {
|
||||
applyId: string;
|
||||
clientsInserted: number;
|
||||
@@ -212,7 +231,14 @@ async function applyInterest(
|
||||
|
||||
let berthId: string | null = null;
|
||||
if (planned.berthMooringNumber) {
|
||||
berthId = mooringToBerthId.get(planned.berthMooringNumber) ?? null;
|
||||
berthId =
|
||||
mooringToBerthId.get(planned.berthMooringNumber) ??
|
||||
// The legacy NocoDB Interests table uses bare mooring strings like
|
||||
// "D32", "B16", whereas the new berths schema (mirroring the NocoDB
|
||||
// Berths snapshot) uses zero-padded "D-32", "B-16". Try the dashed
|
||||
// form as a fallback so legacy references resolve correctly.
|
||||
mooringToBerthId.get(normalizeLegacyMooring(planned.berthMooringNumber)) ??
|
||||
null;
|
||||
if (!berthId) {
|
||||
result.warnings.push(
|
||||
`Interest source=${planned.sourceId} references unknown mooring="${planned.berthMooringNumber}" — interest created without berth link`,
|
||||
|
||||
Reference in New Issue
Block a user