fix: JSONB space-after-colon in all registry LIKE/regex patterns
PostgreSQL JSONB value::text serializes JSON with spaces after colons
("number": "B-2026-00001") but all LIKE patterns searched for the
no-space format ("number":"B-2026-00001"), causing zero matches and
every new entry getting sequence #1.
Fixed in allocateSequenceNumber, recalculateSequence, and debug-sequences.
Added PATCH handler to migrate old-format entries (BTG/SDT/USW/GRP)
to new single-letter format (B/S/U/G).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -140,3 +140,69 @@ export async function POST() {
|
||||
message: "All counters reset from actual entries (both old and new format).",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* PATCH — Migrate old-format entries (BTG/SDT/USW/GRP) to new format (B/S/U/G).
|
||||
* Rewrites the "number" field inside the JSONB value for matching entries.
|
||||
*/
|
||||
export async function PATCH() {
|
||||
const session = await getAuthSession();
|
||||
if (!session) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
// Map old 3-letter prefixes to new single-letter
|
||||
const migrations: Array<{ old: string; new: string }> = [
|
||||
{ old: "BTG", new: "B" },
|
||||
{ old: "SDT", new: "S" },
|
||||
{ old: "USW", new: "U" },
|
||||
{ old: "GRP", new: "G" },
|
||||
];
|
||||
|
||||
const results: Array<{ prefix: string; updated: number }> = [];
|
||||
|
||||
for (const m of migrations) {
|
||||
// Find entries with old-format numbers: BTG-2026-IN-00001, SDT-2026-OUT-00002, etc.
|
||||
const entries = await prisma.$queryRawUnsafe<
|
||||
Array<{ key: string; num: string }>
|
||||
>(`
|
||||
SELECT key,
|
||||
SUBSTRING(value::text FROM '"number": "([^"]+)"') AS num
|
||||
FROM "KeyValueStore"
|
||||
WHERE namespace = 'registratura'
|
||||
AND key LIKE 'entry:%'
|
||||
AND value::text ~ '"number": "${m.old}-[0-9]{4}-(IN|OUT|INT)-[0-9]{5}"'
|
||||
`);
|
||||
|
||||
let updated = 0;
|
||||
for (const entry of entries) {
|
||||
if (!entry.num) continue;
|
||||
|
||||
// Parse: SDT-2026-OUT-00001 → S-2026-00001
|
||||
const match = entry.num.match(
|
||||
new RegExp(`^${m.old}-(\\d{4})-(?:IN|OUT|INT)-(\\d{5})$`)
|
||||
);
|
||||
if (!match) continue;
|
||||
|
||||
const newNumber = `${m.new}-${match[1]}-${match[2]}`;
|
||||
|
||||
// Update the JSONB value — replace the number field
|
||||
await prisma.$executeRawUnsafe(`
|
||||
UPDATE "KeyValueStore"
|
||||
SET value = jsonb_set(value, '{number}', $1::jsonb)
|
||||
WHERE namespace = 'registratura'
|
||||
AND key = $2
|
||||
`, JSON.stringify(newNumber), entry.key);
|
||||
|
||||
updated++;
|
||||
}
|
||||
|
||||
results.push({ prefix: m.old, updated });
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
migrations: results,
|
||||
message: "Old-format entries migrated to new format. Run POST to reset counters.",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -258,6 +258,7 @@ export async function allocateSequenceNumber(
|
||||
|
||||
// New format: B-2026-00001
|
||||
// NOTE: Use [0-9] instead of \d — PostgreSQL POSIX regex may not support \d
|
||||
// NOTE: JSONB value::text serializes with a space after colons ("number": "...")
|
||||
const newLike = `%"number": "${companyPrefix}-${yr}-%"%`;
|
||||
const newRegex = `${companyPrefix}-${yr}-([0-9]{5})`;
|
||||
const newMaxRows = await tx.$queryRaw<Array<{ maxSeq: number | null }>>`
|
||||
@@ -335,6 +336,7 @@ export async function recalculateSequence(
|
||||
|
||||
// Find max from new format (B-2026-00001)
|
||||
// NOTE: Use [0-9] instead of \d — PostgreSQL POSIX regex may not support \d
|
||||
// NOTE: JSONB value::text serializes with a space after colons ("number": "...")
|
||||
const newLike = `%"number": "${companyPrefix}-${yr}-%"%`;
|
||||
const newRegex = `${companyPrefix}-${yr}-([0-9]{5})`;
|
||||
const newRows = await prisma.$queryRaw<Array<{ maxSeq: number | null }>>`
|
||||
|
||||
Reference in New Issue
Block a user