From 86b2d64f5ccfd6c93f1d3b622dba399a5b38e143 Mon Sep 17 00:00:00 2001 From: Replit Agent Date: Thu, 9 Jul 2026 09:17:43 +0000 Subject: [PATCH] Task #735: booking reference digits-only (no letters) - makeBookingReference in artifacts/api-server/src/routes/protocol.ts now returns `${year}-${pad4(id)}` instead of `RB-${year}-${pad4(id)}`. - Backfilled 7 existing dev DB rows: stripped "RB-" prefix via regexp_replace; verified 0 rows contain letters, unique/NOT NULL intact. - lib/db/scripts/pre-push-cleanup.ts updated (architect-review finding): NULL backfill now uses digits-only format, plus a new idempotent normalization step that strips legacy "RB-" prefixes on every push. - Frontend unchanged: bookingNumber() already strips non-digits; details card and public booking success page display the raw reference which is now digits-only. ilike search still works. - tsc clean for tx-os and lib/db; API workflow restarted. --- artifacts/api-server/src/routes/protocol.ts | 6 +++--- lib/db/scripts/pre-push-cleanup.ts | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/artifacts/api-server/src/routes/protocol.ts b/artifacts/api-server/src/routes/protocol.ts index 93d57818..cf7814d0 100644 --- a/artifacts/api-server/src/routes/protocol.ts +++ b/artifacts/api-server/src/routes/protocol.ts @@ -442,10 +442,10 @@ async function lockRoomRow( } // Build a human-friendly booking reference (رقم الحجز) from a row id and its -// creation date, e.g. "RB-2026-0042". Deterministic + unique because the id -// is unique. +// creation date, e.g. "2026-0042". Digits only (no letters, per user +// request). Deterministic + unique because the id is unique. function makeBookingReference(id: number, when: Date): string { - return `RB-${when.getFullYear()}-${String(id).padStart(4, "0")}`; + return `${when.getFullYear()}-${String(id).padStart(4, "0")}`; } // Reserve the next booking id from the table's serial sequence so we can diff --git a/lib/db/scripts/pre-push-cleanup.ts b/lib/db/scripts/pre-push-cleanup.ts index ed98feee..3ba6a33a 100644 --- a/lib/db/scripts/pre-push-cleanup.ts +++ b/lib/db/scripts/pre-push-cleanup.ts @@ -85,8 +85,9 @@ async function main(): Promise { // NOT NULL + unique constraint on protocol_room_bookings.booking_reference. // Without this, pushing the constraint against a populated table (any // pre-existing booking predates the column) would fail. Reference format - // mirrors makeBookingReference() in the API: RB-{year}-{pad4(id)}, which - // is unique because id is unique. Idempotent: only touches NULL rows. + // mirrors makeBookingReference() in the API: {year}-{pad4(id)} (digits + // only, no letters), which is unique because id is unique. Idempotent: + // fills NULL rows and normalizes any legacy "RB-" prefixed rows. if (await tableExists(client, "protocol_room_bookings")) { const hasColumn = await client.query( `SELECT 1 FROM information_schema.columns @@ -98,10 +99,21 @@ async function main(): Promise { const backfilled = await client.query( `UPDATE protocol_room_bookings SET booking_reference = - 'RB-' || EXTRACT(YEAR FROM created_at)::int + EXTRACT(YEAR FROM created_at)::int || '-' || LPAD(id::text, 4, '0') WHERE booking_reference IS NULL`, ); + const normalized = await client.query( + `UPDATE protocol_room_bookings + SET booking_reference = regexp_replace(booking_reference, '^RB-', '') + WHERE booking_reference LIKE 'RB-%'`, + ); + const stripped = normalized.rowCount ?? 0; + if (stripped > 0) { + console.log( + `[pre-push] Normalized ${stripped} legacy RB- booking_reference value(s) to digits-only.`, + ); + } const filled = backfilled.rowCount ?? 0; if (filled > 0) { console.log(