import { MigrationInterface, QueryRunner } from 'typeorm'; function quoteIdentifier(identifier: string): string { return `"${identifier.replaceAll('"', '""')}"`; } export class PhaseD5317VerificationExecution1788199200000 implements MigrationInterface { name = 'PhaseD5317VerificationExecution1788199200000'; public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(` ALTER TABLE inspection_finding_verification_visits ADD COLUMN target_control_on date NULL, ADD COLUMN outcome varchar(24) NULL, ADD COLUMN result_notes text NULL, ADD COLUMN verified_at timestamptz NULL, ADD COLUMN result_recorded_at timestamptz NULL, ADD COLUMN result_recorded_by uuid NULL, ADD COLUMN rescheduled_control_on date NULL, ADD CONSTRAINT chk_inspection_verification_outcome CHECK (outcome IS NULL OR outcome IN ('RESOLVED', 'NOT_RESOLVED', 'REQUIRES_NEW_DATE')), ADD CONSTRAINT fk_inspection_verification_result_user FOREIGN KEY (result_recorded_by) REFERENCES users(id) ON DELETE SET NULL `); await queryRunner.query(` UPDATE inspection_finding_verification_visits verification_link SET target_control_on = finding.next_control_on FROM inspection_findings finding WHERE finding.id = verification_link.finding_id AND verification_link.target_control_on IS NULL `); await queryRunner.query(` CREATE INDEX idx_inspection_finding_verification_result ON inspection_finding_verification_visits (visit_id, outcome, result_recorded_at DESC) `); await queryRunner.query(` ALTER TABLE inspection_finding_evidence DROP CONSTRAINT chk_inspection_finding_evidence_purpose, ADD CONSTRAINT chk_inspection_finding_evidence_purpose CHECK ( purpose IN ( 'OBSERVATION', 'VERIFICATION', 'COMPANY_RESPONSE', 'COMMUNICATION_ATTACHMENT', 'OTHER_DOCUMENT' ) ), ADD COLUMN verification_visit_id uuid NULL, ADD CONSTRAINT fk_inspection_finding_evidence_verification_visit FOREIGN KEY (verification_visit_id) REFERENCES inspection_visits(id) ON DELETE RESTRICT `); await queryRunner.query(` CREATE INDEX idx_inspection_finding_evidence_verification_visit ON inspection_finding_evidence (verification_visit_id, created_at DESC) `); const appRole = process.env.DB_APP_USER; if (!appRole) throw new Error('Missing required environment variable: DB_APP_USER'); const applicationRole = quoteIdentifier(appRole); await queryRunner.query(` GRANT UPDATE ( outcome, result_notes, verified_at, result_recorded_at, result_recorded_by, rescheduled_control_on, updated_at ) ON inspection_finding_verification_visits TO ${applicationRole} `); } public async down(queryRunner: QueryRunner): Promise { const appRole = process.env.DB_APP_USER; if (!appRole) throw new Error('Missing required environment variable: DB_APP_USER'); const applicationRole = quoteIdentifier(appRole); await queryRunner.query(` REVOKE UPDATE ( outcome, result_notes, verified_at, result_recorded_at, result_recorded_by, rescheduled_control_on, updated_at ) ON inspection_finding_verification_visits FROM ${applicationRole} `); await queryRunner.query('DROP INDEX idx_inspection_finding_evidence_verification_visit'); await queryRunner.query(` ALTER TABLE inspection_finding_evidence DROP CONSTRAINT fk_inspection_finding_evidence_verification_visit, DROP CONSTRAINT chk_inspection_finding_evidence_purpose, DROP COLUMN verification_visit_id, ADD CONSTRAINT chk_inspection_finding_evidence_purpose CHECK ( purpose IN ( 'OBSERVATION', 'COMPANY_RESPONSE', 'COMMUNICATION_ATTACHMENT', 'OTHER_DOCUMENT' ) ) `); await queryRunner.query('DROP INDEX idx_inspection_finding_verification_result'); await queryRunner.query(` ALTER TABLE inspection_finding_verification_visits DROP CONSTRAINT fk_inspection_verification_result_user, DROP CONSTRAINT chk_inspection_verification_outcome, DROP COLUMN rescheduled_control_on, DROP COLUMN result_recorded_by, DROP COLUMN result_recorded_at, DROP COLUMN verified_at, DROP COLUMN result_notes, DROP COLUMN outcome, DROP COLUMN target_control_on `); } }