From e4ddb878ff54a0aacaa35b91a7636ab88aa30e8c Mon Sep 17 00:00:00 2001 From: Simone Cavalli Date: Thu, 11 Jun 2026 06:21:35 +0200 Subject: [PATCH] feat(07-unified-service-catalog): add services table to schema with audit trail columns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New `services` pgTable with: id, name, description, unit_price, category, active, migrated_from, migrated_id, created_at - Audit trail columns (migrated_from, migrated_id) enable rollback safety during expand-contract migration - Service and NewService TypeScript types exported - Migration SQL file created (0001_add_services_table.sql) for schema push - No FK relations added yet (Phase 8 will establish connections) - service_catalog, offer_services tables unchanged — legacy compatibility preserved - npm run build passes TypeScript checks Co-Authored-By: Claude Haiku 4.5 --- src/db/migrations/0001_add_services_table.sql | 11 +++++++++ src/db/migrations/meta/_journal.json | 7 ++++++ src/db/schema.ts | 24 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 src/db/migrations/0001_add_services_table.sql diff --git a/src/db/migrations/0001_add_services_table.sql b/src/db/migrations/0001_add_services_table.sql new file mode 100644 index 0000000..594535e --- /dev/null +++ b/src/db/migrations/0001_add_services_table.sql @@ -0,0 +1,11 @@ +CREATE TABLE "services" ( + "id" text PRIMARY KEY NOT NULL, + "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 +); diff --git a/src/db/migrations/meta/_journal.json b/src/db/migrations/meta/_journal.json index bd0887f..8a21a81 100644 --- a/src/db/migrations/meta/_journal.json +++ b/src/db/migrations/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1778705200462, "tag": "0000_pretty_typhoid_mary", "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1781151516000, + "tag": "0001_add_services_table", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/db/schema.ts b/src/db/schema.ts index 4971b04..269a820 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -172,6 +172,28 @@ export const service_catalog = pgTable("service_catalog", { active: boolean("active").notNull().default(true), }); +// ============ SERVICES (UNIFIED CATALOG — replaces service_catalog + offer_services) ============ +// migrated_from/migrated_id provide audit trail for safe rollback during the +// expand-contract migration (Phase 7 expand; Phase 8 begins contract on offer side). +export const services = pgTable("services", { + id: text("id") + .primaryKey() + .$defaultFn(() => nanoid()), + name: text("name").notNull(), + description: text("description"), + unit_price: numeric("unit_price", { precision: 10, scale: 2 }).notNull(), + category: text("category"), + active: boolean("active").notNull().default(true), + migrated_from: text("migrated_from"), // "service_catalog" | "offer_services" | null (new rows after Phase 7) + migrated_id: text("migrated_id"), // original id from source table, null for new rows + created_at: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), +}); + +export const servicesRelations = relations(services, (_) => ({ + // No FK relations yet in Phase 7 — quote_items and offer_micro_services + // continue to reference service_catalog/offer_services until Phase 8. +})); + // ============ QUOTE ITEMS (admin-only — NEVER exposed via client API) ============ export const quote_items = pgTable("quote_items", { id: text("id") @@ -386,6 +408,8 @@ export type Document = typeof documents.$inferSelect; export type NewDocument = typeof documents.$inferInsert; export type Note = typeof notes.$inferSelect; export type NewNote = typeof notes.$inferInsert; +export type Service = typeof services.$inferSelect; +export type NewService = typeof services.$inferInsert; export type ServiceCatalog = typeof service_catalog.$inferSelect; export type NewServiceCatalog = typeof service_catalog.$inferInsert; export type QuoteItem = typeof quote_items.$inferSelect;