diff --git a/db/core/changelog-master.xml b/db/core/changelog-master.xml index d7ec0df..216d163 100644 --- a/db/core/changelog-master.xml +++ b/db/core/changelog-master.xml @@ -50,5 +50,6 @@ + diff --git a/db/core/changesets/041-fn-worker-document-store-replace.sql b/db/core/changesets/041-fn-worker-document-store-replace.sql new file mode 100644 index 0000000..0f98976 --- /dev/null +++ b/db/core/changesets/041-fn-worker-document-store-replace.sql @@ -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; +$$;