feat(07-unified-service-catalog): add services table to schema with audit trail columns

- 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 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 06:21:35 +02:00
parent 23cb057f0b
commit e4ddb878ff
3 changed files with 42 additions and 0 deletions
@@ -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
);
+7
View File
@@ -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
}
]
}
+24
View File
@@ -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;