144 lines
7.4 KiB
TypeScript
144 lines
7.4 KiB
TypeScript
import { MigrationInterface, QueryRunner } from 'typeorm';
|
|
|
|
const permissions = [
|
|
['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;
|
|
|
|
export class PhaseD5322DirectorReportReview1788631200000 implements MigrationInterface {
|
|
name = 'PhaseD5322DirectorReportReview1788631200000';
|
|
|
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
await queryRunner.query(`
|
|
ALTER TABLE inspection_reports
|
|
ADD COLUMN review_status varchar(32) NOT NULL DEFAULT 'PENDING_REVIEW',
|
|
ADD COLUMN current_revision_number integer NOT NULL DEFAULT 0,
|
|
ADD COLUMN approved_revision_id uuid,
|
|
ADD COLUMN approved_by uuid,
|
|
ADD COLUMN approved_at timestamptz,
|
|
ADD COLUMN review_note varchar(1000),
|
|
ADD COLUMN signed_at timestamptz,
|
|
ADD CONSTRAINT chk_inspection_reports_review_status CHECK (review_status IN ('PENDING_REVIEW','APPROVED','SIGNED')),
|
|
ADD CONSTRAINT chk_inspection_reports_revision_number CHECK (current_revision_number >= 0),
|
|
ADD CONSTRAINT fk_inspection_reports_approved_by FOREIGN KEY (approved_by) REFERENCES users(id) ON DELETE SET NULL
|
|
`);
|
|
await queryRunner.query(`
|
|
CREATE TABLE inspection_report_revisions (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
report_id uuid NOT NULL,
|
|
revision_number integer NOT NULL,
|
|
source varchar(24) NOT NULL,
|
|
original_name varchar(255) NOT NULL,
|
|
stored_name varchar(255) NOT NULL,
|
|
mime_type varchar(120) NOT NULL,
|
|
size_bytes integer NOT NULL,
|
|
sha256 char(64) NOT NULL,
|
|
change_summary varchar(1000),
|
|
created_by uuid NOT NULL,
|
|
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT fk_inspection_report_revisions_report FOREIGN KEY (report_id) REFERENCES inspection_reports(id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_inspection_report_revisions_created_by FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE RESTRICT,
|
|
CONSTRAINT uq_inspection_report_revision_number UNIQUE (report_id, revision_number),
|
|
CONSTRAINT chk_inspection_report_revision_number CHECK (revision_number > 0),
|
|
CONSTRAINT chk_inspection_report_revision_source CHECK (source IN ('AUTO','DIRECTOR_UPLOAD')),
|
|
CONSTRAINT chk_inspection_report_revision_file CHECK (
|
|
mime_type = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
AND size_bytes > 0
|
|
AND sha256 ~ '^[0-9a-f]{64}$'
|
|
)
|
|
)
|
|
`);
|
|
await queryRunner.query(`CREATE INDEX idx_inspection_report_revisions_report_created ON inspection_report_revisions (report_id, revision_number DESC)`);
|
|
await queryRunner.query(`
|
|
INSERT INTO inspection_report_revisions (
|
|
report_id, revision_number, source, original_name, stored_name, mime_type,
|
|
size_bytes, sha256, change_summary, created_by, created_at
|
|
)
|
|
SELECT
|
|
id, 1, 'AUTO', word_original_name, word_stored_name, word_mime_type,
|
|
word_size_bytes, word_sha256, 'Versión automática inicial', generated_by,
|
|
COALESCE(word_generated_at, generated_at)
|
|
FROM inspection_reports
|
|
WHERE word_status = 'READY'
|
|
AND word_original_name IS NOT NULL
|
|
AND word_stored_name IS NOT NULL
|
|
AND word_mime_type = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
AND word_size_bytes > 0
|
|
AND word_sha256 ~ '^[0-9a-f]{64}$'
|
|
ON CONFLICT (report_id, revision_number) DO NOTHING
|
|
`);
|
|
await queryRunner.query(`
|
|
UPDATE inspection_reports report
|
|
SET current_revision_number = 1
|
|
WHERE EXISTS (
|
|
SELECT 1 FROM inspection_report_revisions revision
|
|
WHERE revision.report_id = report.id AND revision.revision_number = 1
|
|
)
|
|
`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE inspection_reports
|
|
ADD CONSTRAINT fk_inspection_reports_approved_revision
|
|
FOREIGN KEY (approved_revision_id) REFERENCES inspection_report_revisions(id) ON DELETE RESTRICT
|
|
`);
|
|
await queryRunner.query(`
|
|
CREATE TABLE inspection_report_signatures (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
report_id uuid NOT NULL UNIQUE,
|
|
revision_id uuid NOT NULL UNIQUE,
|
|
signed_by uuid NOT NULL,
|
|
signed_at timestamptz NOT NULL,
|
|
confirmation_text varchar(500) NOT NULL,
|
|
signature_payload jsonb NOT NULL,
|
|
signature_sha256 char(64) NOT NULL,
|
|
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT fk_inspection_report_signatures_report FOREIGN KEY (report_id) REFERENCES inspection_reports(id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_inspection_report_signatures_revision FOREIGN KEY (revision_id) REFERENCES inspection_report_revisions(id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_inspection_report_signatures_user FOREIGN KEY (signed_by) REFERENCES users(id) ON DELETE RESTRICT,
|
|
CONSTRAINT chk_inspection_report_signature_sha CHECK (signature_sha256 ~ '^[0-9a-f]{64}$')
|
|
)
|
|
`);
|
|
await queryRunner.query(`CREATE INDEX idx_inspection_report_signatures_signed_at ON inspection_report_signatures (signed_at DESC)`);
|
|
for (const [code, description] of permissions) {
|
|
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
|
|
`);
|
|
}
|
|
|
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
await queryRunner.query(`DELETE FROM role_permissions rp USING permissions p WHERE rp.permission_id = p.id AND p.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(`DROP TABLE IF EXISTS inspection_report_signatures`);
|
|
await queryRunner.query(`ALTER TABLE inspection_reports DROP CONSTRAINT IF EXISTS fk_inspection_reports_approved_revision`);
|
|
await queryRunner.query(`DROP TABLE IF EXISTS inspection_report_revisions`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE inspection_reports
|
|
DROP CONSTRAINT IF EXISTS fk_inspection_reports_approved_by,
|
|
DROP CONSTRAINT IF EXISTS chk_inspection_reports_revision_number,
|
|
DROP CONSTRAINT IF EXISTS chk_inspection_reports_review_status,
|
|
DROP COLUMN IF EXISTS signed_at,
|
|
DROP COLUMN IF EXISTS review_note,
|
|
DROP COLUMN IF EXISTS approved_at,
|
|
DROP COLUMN IF EXISTS approved_by,
|
|
DROP COLUMN IF EXISTS approved_revision_id,
|
|
DROP COLUMN IF EXISTS current_revision_number,
|
|
DROP COLUMN IF EXISTS review_status
|
|
`);
|
|
}
|
|
}
|