chore: import DH V2 D5.6.4 production baseline
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
const newPermissions = [
|
||||
'inspection_acts.read',
|
||||
'inspection_acts.create',
|
||||
'inspection_acts.update',
|
||||
'inspection_acts.cancel',
|
||||
] as const;
|
||||
|
||||
const rolePermissionValues = `
|
||||
('admin', 'inspection_acts.read'),
|
||||
('admin', 'inspection_acts.create'),
|
||||
('admin', 'inspection_acts.update'),
|
||||
('admin', 'inspection_acts.cancel'),
|
||||
('director', 'inspection_acts.read'),
|
||||
('supervisor', 'inspection_acts.read'),
|
||||
('supervisor', 'inspection_acts.create'),
|
||||
('supervisor', 'inspection_acts.update'),
|
||||
('supervisor', 'inspection_acts.cancel'),
|
||||
('inspector', 'inspection_acts.read'),
|
||||
('inspector', 'inspection_acts.create'),
|
||||
('inspector', 'inspection_acts.update'),
|
||||
('auditor', 'inspection_acts.read')
|
||||
`;
|
||||
|
||||
function quoteIdentifier(identifier: string): string {
|
||||
return `"${identifier.replaceAll('"', '""')}"`;
|
||||
}
|
||||
|
||||
export class PhaseD2InspectionActs1787068800000 implements MigrationInterface {
|
||||
name = 'PhaseD2InspectionActs1787068800000';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE document_annual_sequences (
|
||||
document_type varchar(20) NOT NULL,
|
||||
year integer NOT NULL,
|
||||
last_number integer NOT NULL DEFAULT 0,
|
||||
updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (document_type, year),
|
||||
CONSTRAINT chk_document_annual_sequences_type CHECK (
|
||||
document_type IN ('ACT', 'REPORT')
|
||||
),
|
||||
CONSTRAINT chk_document_annual_sequences_year CHECK (
|
||||
year BETWEEN 2000 AND 9999
|
||||
),
|
||||
CONSTRAINT chk_document_annual_sequences_number CHECK (
|
||||
last_number >= 0
|
||||
)
|
||||
)
|
||||
`);
|
||||
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE inspection_acts (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
visit_id uuid NOT NULL,
|
||||
act_year integer NOT NULL,
|
||||
act_number integer NOT NULL,
|
||||
code varchar(24) NOT NULL,
|
||||
status varchar(24) NOT NULL DEFAULT 'DRAFT',
|
||||
occurred_at timestamptz NOT NULL,
|
||||
title varchar(200) NOT NULL,
|
||||
summary text NOT NULL,
|
||||
observations text,
|
||||
current_version integer NOT NULL DEFAULT 0,
|
||||
cancellation_reason text,
|
||||
created_by uuid,
|
||||
updated_by uuid,
|
||||
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT uq_inspection_acts_year_number UNIQUE (act_year, act_number),
|
||||
CONSTRAINT uq_inspection_acts_code UNIQUE (code),
|
||||
CONSTRAINT chk_inspection_acts_year CHECK (act_year BETWEEN 2000 AND 9999),
|
||||
CONSTRAINT chk_inspection_acts_number CHECK (act_number > 0),
|
||||
CONSTRAINT chk_inspection_acts_code CHECK (
|
||||
code ~ '^ACTA-[0-9]{4}-[0-9]{6}$'
|
||||
),
|
||||
CONSTRAINT chk_inspection_acts_status CHECK (
|
||||
status IN ('DRAFT', 'READY', 'CLOSED', 'CANCELLED', 'RECTIFIED')
|
||||
),
|
||||
CONSTRAINT chk_inspection_acts_current_version CHECK (current_version >= 0),
|
||||
CONSTRAINT chk_inspection_acts_cancellation CHECK (
|
||||
status <> 'CANCELLED' OR LENGTH(TRIM(COALESCE(cancellation_reason, ''))) >= 10
|
||||
),
|
||||
CONSTRAINT fk_inspection_acts_visit FOREIGN KEY (visit_id)
|
||||
REFERENCES inspection_visits(id) ON DELETE RESTRICT,
|
||||
CONSTRAINT fk_inspection_acts_created_by FOREIGN KEY (created_by)
|
||||
REFERENCES users(id) ON DELETE SET NULL,
|
||||
CONSTRAINT fk_inspection_acts_updated_by FOREIGN KEY (updated_by)
|
||||
REFERENCES users(id) ON DELETE SET NULL
|
||||
)
|
||||
`);
|
||||
await queryRunner.query(`CREATE INDEX idx_inspection_acts_visit_status ON inspection_acts (visit_id, status)`);
|
||||
await queryRunner.query(`CREATE INDEX idx_inspection_acts_occurred_at ON inspection_acts (occurred_at)`);
|
||||
await queryRunner.query(`CREATE INDEX idx_inspection_acts_created_by ON inspection_acts (created_by)`);
|
||||
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE inspection_act_assets (
|
||||
act_id uuid NOT NULL,
|
||||
asset_id uuid NOT NULL,
|
||||
included boolean NOT NULL DEFAULT true,
|
||||
added_by uuid,
|
||||
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (act_id, asset_id),
|
||||
CONSTRAINT fk_inspection_act_assets_act FOREIGN KEY (act_id)
|
||||
REFERENCES inspection_acts(id) ON DELETE RESTRICT,
|
||||
CONSTRAINT fk_inspection_act_assets_asset FOREIGN KEY (asset_id)
|
||||
REFERENCES assets(id) ON DELETE RESTRICT,
|
||||
CONSTRAINT fk_inspection_act_assets_added_by FOREIGN KEY (added_by)
|
||||
REFERENCES users(id) ON DELETE SET NULL
|
||||
)
|
||||
`);
|
||||
await queryRunner.query(`CREATE INDEX idx_inspection_act_assets_asset_id ON inspection_act_assets (asset_id)`);
|
||||
await queryRunner.query(`CREATE INDEX idx_inspection_act_assets_included ON inspection_act_assets (act_id, included)`);
|
||||
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE inspection_act_versions (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
act_id uuid NOT NULL,
|
||||
version_number integer NOT NULL,
|
||||
event varchar(24) NOT NULL,
|
||||
snapshot jsonb NOT NULL,
|
||||
actor_user_id uuid,
|
||||
actor_username varchar(80),
|
||||
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT uq_inspection_act_versions_number UNIQUE (act_id, version_number),
|
||||
CONSTRAINT chk_inspection_act_versions_number CHECK (version_number > 0),
|
||||
CONSTRAINT chk_inspection_act_versions_event CHECK (
|
||||
event IN ('CREATED', 'UPDATED', 'CANCELLED')
|
||||
),
|
||||
CONSTRAINT fk_inspection_act_versions_act FOREIGN KEY (act_id)
|
||||
REFERENCES inspection_acts(id) ON DELETE RESTRICT,
|
||||
CONSTRAINT fk_inspection_act_versions_actor FOREIGN KEY (actor_user_id)
|
||||
REFERENCES users(id) ON DELETE SET NULL
|
||||
)
|
||||
`);
|
||||
await queryRunner.query(`CREATE INDEX idx_inspection_act_versions_created_at ON inspection_act_versions (created_at)`);
|
||||
|
||||
await queryRunner.query(`
|
||||
INSERT INTO permissions (code, description)
|
||||
VALUES
|
||||
('inspection_acts.read', 'Consultar actas y sus versiones'),
|
||||
('inspection_acts.create', 'Crear actas dentro de visitas asignadas'),
|
||||
('inspection_acts.update', 'Actualizar actas en borrador'),
|
||||
('inspection_acts.cancel', 'Cancelar actas en borrador')
|
||||
ON CONFLICT (code) DO UPDATE SET description = EXCLUDED.description
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
WITH mapping(role_code, permission_code) AS (VALUES ${rolePermissionValues})
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT role.id, permission.id
|
||||
FROM mapping
|
||||
INNER JOIN roles role ON role.code = mapping.role_code
|
||||
INNER JOIN permissions permission ON permission.code = mapping.permission_code
|
||||
ON CONFLICT (role_id, permission_id) DO NOTHING
|
||||
`);
|
||||
|
||||
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 SELECT, INSERT, UPDATE ON TABLE
|
||||
document_annual_sequences, inspection_acts, inspection_act_assets
|
||||
TO ${applicationRole}
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
GRANT SELECT, INSERT ON TABLE inspection_act_versions
|
||||
TO ${applicationRole}
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
REVOKE DELETE ON TABLE
|
||||
document_annual_sequences, inspection_acts,
|
||||
inspection_act_assets, inspection_act_versions
|
||||
FROM ${applicationRole}
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
REVOKE UPDATE ON TABLE inspection_act_versions
|
||||
FROM ${applicationRole}
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
WITH mapping(role_code, permission_code) AS (VALUES ${rolePermissionValues})
|
||||
DELETE FROM role_permissions role_permission
|
||||
USING roles role, permissions permission, mapping
|
||||
WHERE role_permission.role_id = role.id
|
||||
AND role_permission.permission_id = permission.id
|
||||
AND role.code = mapping.role_code
|
||||
AND permission.code = mapping.permission_code
|
||||
`);
|
||||
await queryRunner.query(
|
||||
`DELETE FROM permissions WHERE code = ANY($1::varchar[])
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM role_permissions WHERE permission_id = permissions.id
|
||||
)`,
|
||||
[newPermissions],
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE inspection_act_versions`);
|
||||
await queryRunner.query(`DROP TABLE inspection_act_assets`);
|
||||
await queryRunner.query(`DROP TABLE inspection_acts`);
|
||||
await queryRunner.query(`DROP TABLE document_annual_sequences`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user