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.
This commit is contained in:
Replit Agent
2026-07-09 09:17:43 +00:00
parent 1d2ff5e6b9
commit 86b2d64f5c
2 changed files with 18 additions and 6 deletions
+3 -3
View File
@@ -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
+15 -3
View File
@@ -85,8 +85,9 @@ async function main(): Promise<void> {
// 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<void> {
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(