Add procedures schema: templates, steps, instances, 7 audit events
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+101
-1
@@ -1044,6 +1044,99 @@ export const companyServiceAccounts = pgTable(
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// ── Procedures & Checklists ────────────────────────────
|
||||||
|
|
||||||
|
export const procedureInstanceStatusEnum = pgEnum('procedure_instance_status', [
|
||||||
|
'in_progress',
|
||||||
|
'completed',
|
||||||
|
'cancelled'
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const procedureTemplates = pgTable(
|
||||||
|
'procedure_templates',
|
||||||
|
{
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
companyId: uuid('company_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => companies.id, { onDelete: 'cascade' }),
|
||||||
|
title: text('title').notNull(),
|
||||||
|
description: text('description'),
|
||||||
|
category: text('category'),
|
||||||
|
isPublished: boolean('is_published').notNull().default(false),
|
||||||
|
createdBy: text('created_by').references(() => users.id, { onDelete: 'set null' }),
|
||||||
|
deletedAt: timestamp('deleted_at', { withTimezone: true }),
|
||||||
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow()
|
||||||
|
},
|
||||||
|
(table) => [index('procedure_templates_company_idx').on(table.companyId)]
|
||||||
|
);
|
||||||
|
|
||||||
|
export const procedureSteps = pgTable(
|
||||||
|
'procedure_steps',
|
||||||
|
{
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
templateId: uuid('template_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => procedureTemplates.id, { onDelete: 'cascade' }),
|
||||||
|
stepNumber: integer('step_number').notNull(),
|
||||||
|
title: text('title').notNull(),
|
||||||
|
description: text('description'),
|
||||||
|
assigneeRole: text('assignee_role'),
|
||||||
|
estimatedMinutes: integer('estimated_minutes'),
|
||||||
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow()
|
||||||
|
},
|
||||||
|
(table) => [
|
||||||
|
uniqueIndex('procedure_steps_template_step_idx').on(table.templateId, table.stepNumber)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
export const procedureInstances = pgTable(
|
||||||
|
'procedure_instances',
|
||||||
|
{
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
templateId: uuid('template_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => procedureTemplates.id, { onDelete: 'restrict' }),
|
||||||
|
companyId: uuid('company_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => companies.id, { onDelete: 'cascade' }),
|
||||||
|
title: text('title').notNull(),
|
||||||
|
status: procedureInstanceStatusEnum('status').notNull().default('in_progress'),
|
||||||
|
startedBy: text('started_by').references(() => users.id, { onDelete: 'set null' }),
|
||||||
|
completedAt: timestamp('completed_at', { withTimezone: true }),
|
||||||
|
cancelledAt: timestamp('cancelled_at', { withTimezone: true }),
|
||||||
|
notes: text('notes'),
|
||||||
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow()
|
||||||
|
},
|
||||||
|
(table) => [
|
||||||
|
index('procedure_instances_company_status_idx').on(table.companyId, table.status)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
export const procedureInstanceSteps = pgTable(
|
||||||
|
'procedure_instance_steps',
|
||||||
|
{
|
||||||
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
|
instanceId: uuid('instance_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => procedureInstances.id, { onDelete: 'cascade' }),
|
||||||
|
stepId: uuid('step_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => procedureSteps.id, { onDelete: 'restrict' }),
|
||||||
|
stepNumber: integer('step_number').notNull(),
|
||||||
|
title: text('title').notNull(),
|
||||||
|
description: text('description'),
|
||||||
|
isCompleted: boolean('is_completed').notNull().default(false),
|
||||||
|
completedBy: text('completed_by').references(() => users.id, { onDelete: 'set null' }),
|
||||||
|
completedAt: timestamp('completed_at', { withTimezone: true }),
|
||||||
|
notes: text('notes')
|
||||||
|
},
|
||||||
|
(table) => [
|
||||||
|
index('procedure_instance_steps_instance_idx').on(table.instanceId, table.stepNumber)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
export const companyAddresses = pgTable(
|
export const companyAddresses = pgTable(
|
||||||
'company_addresses',
|
'company_addresses',
|
||||||
{
|
{
|
||||||
@@ -1147,7 +1240,14 @@ export const companyLogEventEnum = pgEnum('company_log_event', [
|
|||||||
'recurring_bill_posted',
|
'recurring_bill_posted',
|
||||||
'service_account_created',
|
'service_account_created',
|
||||||
'service_account_updated',
|
'service_account_updated',
|
||||||
'service_account_deleted'
|
'service_account_deleted',
|
||||||
|
'procedure_template_created',
|
||||||
|
'procedure_template_updated',
|
||||||
|
'procedure_template_deleted',
|
||||||
|
'procedure_instance_started',
|
||||||
|
'procedure_step_completed',
|
||||||
|
'procedure_instance_completed',
|
||||||
|
'procedure_instance_cancelled'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const companyLog = pgTable(
|
export const companyLog = pgTable(
|
||||||
|
|||||||
Reference in New Issue
Block a user