fix(padron): archivar todas las versiones al reemplazar documento por tipo

Co-authored-by: alberto.martinez <alberto.martinez@mrdev.mx>
This commit is contained in:
Cursor Agent 2026-09-16 21:11:13 +00:00
parent 17d9393b9e
commit 191d0fda3d
No known key found for this signature in database
2 changed files with 154 additions and 0 deletions

View file

@ -50,5 +50,6 @@
<include file="changesets/038-fn-work-program-vs-cost.sql" relativeToChangelogFile="true"/> <include file="changesets/038-fn-work-program-vs-cost.sql" relativeToChangelogFile="true"/>
<include file="changesets/039-fn-next-project-code-global.sql" relativeToChangelogFile="true"/> <include file="changesets/039-fn-next-project-code-global.sql" relativeToChangelogFile="true"/>
<include file="changesets/040-fn-worker-document-delete.sql" relativeToChangelogFile="true"/> <include file="changesets/040-fn-worker-document-delete.sql" relativeToChangelogFile="true"/>
<include file="changesets/041-fn-worker-document-store-replace.sql" relativeToChangelogFile="true"/>
</databaseChangeLog> </databaseChangeLog>

View file

@ -0,0 +1,153 @@
--liquibase formatted sql
-- PANELS · core · reemplazo de documento por tipo (siempre archiva versiones previas)
--changeset panel:core-041a-fn-worker-document-store-replace splitStatements:false
CREATE OR REPLACE FUNCTION core.fn_worker_document_store(payload jsonb)
RETURNS jsonb
LANGUAGE plpgsql
SECURITY INVOKER
SET search_path = core
AS $$
DECLARE
v_worker_id bigint := NULLIF(payload->>'worker_id', '')::bigint;
v_type_code text := btrim(COALESCE(payload->>'type_code', payload->>'type', ''));
v_original_name text := btrim(COALESCE(payload->>'original_name', payload->>'filename', ''));
v_mime text := btrim(COALESCE(payload->>'mime', ''));
v_size_bytes bigint := NULLIF(payload->>'size_bytes', '')::bigint;
v_sha256 text := btrim(COALESCE(payload->>'sha256', ''));
v_iv text := btrim(COALESCE(payload->>'iv', ''));
v_storage_name text := btrim(COALESCE(payload->>'storage_name', ''));
v_uploaded_by_id integer := NULLIF(payload->>'uploaded_by_id', '')::integer;
v_uploaded_by_name text := COALESCE(NULLIF(btrim(payload->>'uploaded_by_name'), ''), '');
v_issued_at date := NULLIF(btrim(payload->>'issued_at'), '')::date;
v_expires_at date := NULLIF(btrim(payload->>'expires_at'), '')::date;
v_imss_company_id integer := NULLIF(payload->>'imss_company_id', '')::integer;
v_imss_alta_at date := NULLIF(btrim(payload->>'imss_alta_at'), '')::date;
v_imss_baja_at date := NULLIF(btrim(payload->>'imss_baja_at'), '')::date;
v_today date := current_date;
v_movement_date date;
v_doc_id bigint;
v_company record;
BEGIN
IF v_worker_id IS NULL THEN
RETURN core.rpc_err(
'VALIDATION',
'fn_worker_document_store: worker_id es obligatorio',
jsonb_build_object('fn', 'fn_worker_document_store', 'field', 'worker_id')
);
END IF;
IF NOT EXISTS (SELECT 1 FROM workers WHERE id = v_worker_id) THEN
RETURN core.rpc_err(
'NOT_FOUND',
format('fn_worker_document_store: trabajador id=%s no encontrado', v_worker_id),
jsonb_build_object('fn', 'fn_worker_document_store', 'worker_id', v_worker_id)
);
END IF;
IF v_type_code = '' THEN
RETURN core.rpc_err(
'VALIDATION',
'fn_worker_document_store: type_code es obligatorio',
jsonb_build_object('fn', 'fn_worker_document_store', 'field', 'type_code')
);
END IF;
IF NOT EXISTS (SELECT 1 FROM document_types WHERE code = v_type_code) THEN
RETURN core.rpc_err(
'VALIDATION',
format('fn_worker_document_store: tipo de documento no válido (%s)', v_type_code),
jsonb_build_object('fn', 'fn_worker_document_store', 'type_code', v_type_code)
);
END IF;
IF v_original_name = '' OR v_mime = '' OR v_sha256 = '' OR v_iv = '' OR v_storage_name = '' THEN
RETURN core.rpc_err(
'VALIDATION',
'fn_worker_document_store: original_name, mime, sha256, iv y storage_name son obligatorios tras la carga a almacenamiento',
jsonb_build_object('fn', 'fn_worker_document_store')
);
END IF;
IF v_size_bytes IS NULL OR v_size_bytes < 0 THEN
RETURN core.rpc_err(
'VALIDATION',
'fn_worker_document_store: size_bytes debe ser un entero no negativo',
jsonb_build_object('fn', 'fn_worker_document_store', 'field', 'size_bytes')
);
END IF;
v_imss_alta_at := COALESCE(v_imss_alta_at, CASE WHEN v_type_code = 'alta_imss' THEN v_today END);
v_imss_baja_at := COALESCE(v_imss_baja_at, CASE WHEN v_type_code = 'baja_imss' THEN v_today END);
v_movement_date := CASE WHEN v_type_code = 'baja_imss' THEN v_imss_baja_at ELSE v_imss_alta_at END;
UPDATE documents
SET is_current = false
WHERE worker_id = v_worker_id
AND type_code = v_type_code;
INSERT INTO documents (
worker_id, type_code, original_name, mime, size_bytes, sha256, iv, storage_name,
is_current, parse_status, issued_at, expires_at, imss_company_id, imss_alta_at,
uploaded_by_id, uploaded_by_name
) VALUES (
v_worker_id, v_type_code, v_original_name, v_mime, v_size_bytes, v_sha256, v_iv, v_storage_name,
true, 'manual',
COALESCE(v_issued_at, v_movement_date),
v_expires_at,
v_imss_company_id,
v_movement_date,
v_uploaded_by_id,
v_uploaded_by_name
)
RETURNING id INTO v_doc_id;
IF v_type_code = 'alta_imss' THEN
v_imss_company_id := COALESCE(
v_imss_company_id,
(SELECT company_id FROM workers WHERE id = v_worker_id)
);
SELECT id, code INTO v_company FROM companies WHERE id = v_imss_company_id;
IF NOT FOUND THEN
RETURN core.rpc_err(
'VALIDATION',
'fn_worker_document_store: empresa patrón no válida para alta IMSS',
jsonb_build_object('fn', 'fn_worker_document_store', 'worker_id', v_worker_id)
);
END IF;
UPDATE workers SET
imss_status = 'alta',
imss_company_id = v_company.id,
imss_alta_at = v_imss_alta_at,
imss_baja_at = NULL,
company_id = v_company.id,
hire_type = v_company.code,
updated_at = now()
WHERE id = v_worker_id;
END IF;
IF v_type_code = 'baja_imss' THEN
UPDATE workers SET
imss_status = 'baja_imss',
imss_baja_at = v_imss_baja_at,
imss_company_id = NULL,
company_id = NULL,
hire_type = '',
updated_at = now()
WHERE id = v_worker_id;
END IF;
IF (SELECT status FROM workers WHERE id = v_worker_id) = 'baja' THEN
UPDATE workers SET pipeline_status = 'baja' WHERE id = v_worker_id;
END IF;
RETURN core.rpc_created(
jsonb_build_object(
'id', v_doc_id,
'worker_id', v_worker_id,
'type_code', v_type_code,
'is_current', true,
'storage_name', v_storage_name
),
format('Metadatos del documento %s guardados para trabajador %s (versión actual)', v_type_code, v_worker_id),
jsonb_build_object('fn', 'fn_worker_document_store', 'id', v_doc_id, 'worker_id', v_worker_id)
);
EXCEPTION WHEN OTHERS THEN
RETURN core.rpc_from_exception('fn_worker_document_store', SQLSTATE, SQLERRM);
END;
$$;