From 34dcd684823208a089ea1af65d7a9298f087aaf7 Mon Sep 17 00:00:00 2001 From: enlineawork Date: Mon, 7 Sep 2026 14:06:47 -0300 Subject: [PATCH] =?UTF-8?q?F3.1:=20agregar=20merge=20cronol=C3=B3gico=20de?= =?UTF-8?q?=20Inventario?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...00-phase-f3-1-asset-chronological-merge.ts | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 api-v3/src/database/migrations/1789844600000-phase-f3-1-asset-chronological-merge.ts diff --git a/api-v3/src/database/migrations/1789844600000-phase-f3-1-asset-chronological-merge.ts b/api-v3/src/database/migrations/1789844600000-phase-f3-1-asset-chronological-merge.ts new file mode 100644 index 0000000..26042b0 --- /dev/null +++ b/api-v3/src/database/migrations/1789844600000-phase-f3-1-asset-chronological-merge.ts @@ -0,0 +1,48 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class PhaseF31AssetChronologicalMerge1789844600000 implements MigrationInterface { + name = 'PhaseF31AssetChronologicalMerge1789844600000'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE asset_merges ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + source_asset_id uuid NOT NULL REFERENCES assets(id) ON DELETE RESTRICT, + canonical_asset_id uuid NOT NULL REFERENCES assets(id) ON DELETE RESTRICT, + reason text NOT NULL, + merged_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, + merged_by uuid NULL REFERENCES users(id) ON DELETE SET NULL, + source varchar(32) NOT NULL, + request_id varchar(128) NULL, + source_snapshot jsonb NOT NULL, + canonical_snapshot jsonb NOT NULL, + CONSTRAINT chk_asset_merges_distinct CHECK (source_asset_id <> canonical_asset_id), + CONSTRAINT uq_asset_merges_source UNIQUE (source_asset_id) + ) + `); + await queryRunner.query(`CREATE INDEX idx_asset_merges_canonical ON asset_merges(canonical_asset_id)`); + await queryRunner.query(`CREATE INDEX idx_asset_merges_merged_at ON asset_merges(merged_at)`); + + await queryRunner.query(` + CREATE OR REPLACE FUNCTION protect_asset_merges_append_only() + RETURNS trigger + LANGUAGE plpgsql + AS $$ + BEGIN + RAISE EXCEPTION 'asset_merges es append-only; una fusión no puede editarse ni borrarse'; + END; + $$ + `); + await queryRunner.query(` + CREATE TRIGGER trg_asset_merges_append_only + BEFORE UPDATE OR DELETE ON asset_merges + FOR EACH ROW EXECUTE FUNCTION protect_asset_merges_append_only() + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query('DROP TRIGGER IF EXISTS trg_asset_merges_append_only ON asset_merges'); + await queryRunner.query('DROP FUNCTION IF EXISTS protect_asset_merges_append_only()'); + await queryRunner.query('DROP TABLE IF EXISTS asset_merges'); + } +}