feat(07-unified-service-catalog): add migration and validation scripts
Task 2: Idempotent backfill script (migrate-services.ts) - Migrates service_catalog (21 rows) → services with migrated_from='service_catalog' - Migrates offer_services (35 rows) → services with migrated_from='offer_services' - Name collision detection: appends ' (Offer)' suffix to prevent overwrites - Idempotent via (migrated_from, migrated_id) lookups — re-runs are safe no-ops - Categorizes services: 'catalog' for operational pricing, 'offer' for marketing pricing - Maps service_catalog.description → services.description - Maps offer_services.transformation_description → services.description - Maps service_catalog.unit_price → services.unit_price - Maps offer_services.price → services.unit_price Task 3: Validation script (validate-services-migration.ts) - Check 1: Row counts match (service_catalog/offer_services rows == migrated rows) - Check 2: No orphaned migrated_id references (all point to existing source rows) - Check 3: Existing quote_items.service_id FKs remain valid (untouched) - Check 4: Existing offer_micro_services.service_id FKs remain valid (untouched) - Check 5: Report any unresolved name collisions (post-migration integrity) - Exits 0 only if all checks PASS Plus: push-services-migration.ts helper for direct SQL execution when drizzle-kit unavailable. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import postgres from "postgres";
|
||||
import { readFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
|
||||
async function push() {
|
||||
const databaseUrl = process.env.DATABASE_URL;
|
||||
if (!databaseUrl) {
|
||||
console.error("DATABASE_URL environment variable is required");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const client = postgres(databaseUrl);
|
||||
|
||||
try {
|
||||
console.log("Pushing services table migration...");
|
||||
|
||||
// Create the services table
|
||||
await client`
|
||||
CREATE TABLE IF NOT EXISTS services (
|
||||
id text PRIMARY KEY,
|
||||
name text NOT NULL,
|
||||
description text,
|
||||
unit_price numeric(10, 2) NOT NULL,
|
||||
category text,
|
||||
active boolean DEFAULT true NOT NULL,
|
||||
migrated_from text,
|
||||
migrated_id text,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL
|
||||
)
|
||||
`;
|
||||
|
||||
console.log("✓ services table created successfully");
|
||||
process.exit(0);
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof Error) {
|
||||
if (err.message.includes("already exists")) {
|
||||
console.log("✓ services table already exists (skipped)");
|
||||
process.exit(0);
|
||||
}
|
||||
console.error("Error pushing migration:", err.message);
|
||||
} else {
|
||||
console.error("Unknown error:", err);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
push();
|
||||
Reference in New Issue
Block a user