import { MigrationInterface, QueryRunner } from 'typeorm'; const legacyDirectorPermissions = [ ['inspection_reports.revise', 'Cargar versiones corregidas del informe para revisión directiva'], ['inspection_reports.review', 'Aprobar la versión vigente del informe'], ['inspection_reports.sign_final', 'Firmar electrónicamente el informe final aprobado'], ] as const; /** * Retira definitivamente el circuito activo legacy Director -> revisión -> aprobación -> firma. * * current_revision_number e inspection_report_revisions se conservan porque forman parte del * versionado útil del INF. inspection_report_signatures también se conserva como archivo * histórico: F4 quita sus endpoints/permisos pero no destruye evidencia documental ya emitida. */ export class F4RemoveDirectorReportLegacy1790000400000 implements MigrationInterface { name = 'F4RemoveDirectorReportLegacy1790000400000'; public async up(queryRunner: QueryRunner): Promise { // Las entregas al Director pertenecían al flujo retirado y no deben seguir // apareciendo como pendientes/reintentables en F4. await queryRunner.query(` DELETE FROM inspection_document_deliveries WHERE recipient_kind = 'DIRECTOR' `); // Retira permisos huérfanos del circuito anterior para que tampoco aparezcan // como capacidades administrables en RBAC una vez eliminados sus endpoints. await queryRunner.query(` DELETE FROM role_permissions WHERE permission_id IN ( SELECT id FROM permissions WHERE code IN ( 'inspection_reports.revise', 'inspection_reports.review', 'inspection_reports.sign_final' ) ) `); await queryRunner.query(` DELETE FROM permissions WHERE code IN ( 'inspection_reports.revise', 'inspection_reports.review', 'inspection_reports.sign_final' ) `); await queryRunner.query(`ALTER TABLE inspection_reports DROP COLUMN IF EXISTS approved_revision_id`); await queryRunner.query(`ALTER TABLE inspection_reports DROP COLUMN IF EXISTS approved_by`); await queryRunner.query(`ALTER TABLE inspection_reports DROP COLUMN IF EXISTS approved_at`); await queryRunner.query(`ALTER TABLE inspection_reports DROP COLUMN IF EXISTS review_note`); await queryRunner.query(`ALTER TABLE inspection_reports DROP COLUMN IF EXISTS signed_at`); await queryRunner.query(`ALTER TABLE inspection_reports DROP COLUMN IF EXISTS review_status`); } public async down(queryRunner: QueryRunner): Promise { // El rollback reconstruye el contrato técnico legacy, pero no recrea entregas Director // ni valores de aprobación eliminados. El backup PRE-F4 sigue siendo la fuente de // recuperación de esos datos históricos si alguna vez fuera necesario volver al circuito. await queryRunner.query(` ALTER TABLE inspection_reports ADD COLUMN IF NOT EXISTS review_status varchar(32) NOT NULL DEFAULT 'PENDING_REVIEW' `); await queryRunner.query(`ALTER TABLE inspection_reports ADD COLUMN IF NOT EXISTS approved_revision_id uuid`); await queryRunner.query(`ALTER TABLE inspection_reports ADD COLUMN IF NOT EXISTS approved_by uuid`); await queryRunner.query(`ALTER TABLE inspection_reports ADD COLUMN IF NOT EXISTS approved_at timestamptz`); await queryRunner.query(`ALTER TABLE inspection_reports ADD COLUMN IF NOT EXISTS review_note varchar(1000)`); await queryRunner.query(`ALTER TABLE inspection_reports ADD COLUMN IF NOT EXISTS signed_at timestamptz`); await queryRunner.query(` DO $$ BEGIN ALTER TABLE inspection_reports ADD CONSTRAINT chk_inspection_reports_review_status CHECK (review_status IN ('PENDING_REVIEW','APPROVED','SIGNED')); EXCEPTION WHEN duplicate_object THEN NULL; END $$; `); await queryRunner.query(` DO $$ BEGIN ALTER TABLE inspection_reports ADD CONSTRAINT fk_inspection_reports_approved_by FOREIGN KEY (approved_by) REFERENCES users(id) ON DELETE SET NULL; EXCEPTION WHEN duplicate_object THEN NULL; END $$; `); await queryRunner.query(` DO $$ BEGIN ALTER TABLE inspection_reports ADD CONSTRAINT fk_inspection_reports_approved_revision FOREIGN KEY (approved_revision_id) REFERENCES inspection_report_revisions(id) ON DELETE RESTRICT; EXCEPTION WHEN duplicate_object THEN NULL; END $$; `); for (const [code, description] of legacyDirectorPermissions) { await queryRunner.query( `INSERT INTO permissions (code, description) VALUES ($1, $2) ON CONFLICT (code) DO UPDATE SET description = EXCLUDED.description`, [code, description], ); } await queryRunner.query(` WITH mapping(role_code, permission_code) AS (VALUES ('director', 'inspection_reports.revise'), ('director', 'inspection_reports.review'), ('director', 'inspection_reports.sign_final') ) INSERT INTO role_permissions (role_id, permission_id) SELECT role.id, permission.id FROM mapping JOIN roles role ON role.code = mapping.role_code JOIN permissions permission ON permission.code = mapping.permission_code ON CONFLICT (role_id, permission_id) DO NOTHING `); } }