mirror of
https://origin.cursor.com/mrdevmx/panels.git
synced 2026-10-09 14:13:18 +00:00
fix: abrir documentos de proyecto enviando doc_id a la RPC
La API pedía el PDF con { id } y fn_project_document_get leía
payload->>'doc_id', así que el id llegaba vacío aunque el archivo
ya estuviera en el bucket. Mismo desajuste en empresa y trabajador.
Co-authored-by: alberto.martinez <alberto.martinez@mrdev.mx>
This commit is contained in:
parent
0d660c4549
commit
de07975d4a
5 changed files with 133 additions and 4 deletions
10
api/document_rpc.ts
Normal file
10
api/document_rpc.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
/**
|
||||||
|
* Payload para core.fn_*_document_get.
|
||||||
|
* Las RPC leen `doc_id`; historicamente la API enviaba `id` y el documento
|
||||||
|
* nunca se encontraba (mensaje "documento id= no encontrado").
|
||||||
|
*/
|
||||||
|
export function normalizeDocumentGetPayload(
|
||||||
|
payload: Record<string, unknown>,
|
||||||
|
): Record<string, unknown> {
|
||||||
|
return { ...payload, doc_id: payload.doc_id ?? payload.id };
|
||||||
|
}
|
||||||
13
api/document_rpc_test.ts
Normal file
13
api/document_rpc_test.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { assertEquals } from "jsr:@std/assert@1";
|
||||||
|
import { normalizeDocumentGetPayload } from "./document_rpc.ts";
|
||||||
|
|
||||||
|
Deno.test("document get payload usa doc_id cuando la API envía id", () => {
|
||||||
|
const payload = normalizeDocumentGetPayload({ project_id: 1, id: 42 });
|
||||||
|
assertEquals(payload.doc_id, 42);
|
||||||
|
assertEquals(payload.project_id, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test("document get payload respeta doc_id si ya viene", () => {
|
||||||
|
const payload = normalizeDocumentGetPayload({ company_id: 3, doc_id: 7, id: 99 });
|
||||||
|
assertEquals(payload.doc_id, 7);
|
||||||
|
});
|
||||||
14
api/main.ts
14
api/main.ts
|
|
@ -16,6 +16,7 @@ import {
|
||||||
type Db,
|
type Db,
|
||||||
} from "./db.ts";
|
} from "./db.ts";
|
||||||
import { callCoreFn } from "./rpc.ts";
|
import { callCoreFn } from "./rpc.ts";
|
||||||
|
import { normalizeDocumentGetPayload } from "./document_rpc.ts";
|
||||||
import {
|
import {
|
||||||
respondRpc,
|
respondRpc,
|
||||||
respondApiError,
|
respondApiError,
|
||||||
|
|
@ -100,7 +101,12 @@ async function rpcDocumentForDownload(
|
||||||
payload: Record<string, unknown>,
|
payload: Record<string, unknown>,
|
||||||
route: string,
|
route: string,
|
||||||
): Promise<{ doc?: StorageDoc; envelope: RpcEnvelope }> {
|
): Promise<{ doc?: StorageDoc; envelope: RpcEnvelope }> {
|
||||||
const envelope = await callCoreFn<{ document: StorageDoc }>(db, fn, payload, { route });
|
const envelope = await callCoreFn<{ document: StorageDoc }>(
|
||||||
|
db,
|
||||||
|
fn,
|
||||||
|
normalizeDocumentGetPayload(payload),
|
||||||
|
{ route },
|
||||||
|
);
|
||||||
if (!envelope.ok) return { envelope };
|
if (!envelope.ok) return { envelope };
|
||||||
const doc = envelope.data?.document;
|
const doc = envelope.data?.document;
|
||||||
if (!doc?.storage_name) {
|
if (!doc?.storage_name) {
|
||||||
|
|
@ -447,7 +453,7 @@ app.get("/v1/companies/:id/documents/:docId", ...requireCoreAuth, requirePermiss
|
||||||
const { doc, envelope } = await rpcDocumentForDownload(
|
const { doc, envelope } = await rpcDocumentForDownload(
|
||||||
db,
|
db,
|
||||||
"core.fn_company_document_get",
|
"core.fn_company_document_get",
|
||||||
{ company_id: companyId, id: docId },
|
{ company_id: companyId, doc_id: docId },
|
||||||
route,
|
route,
|
||||||
);
|
);
|
||||||
if (!doc) return respondRpc(c, envelope);
|
if (!doc) return respondRpc(c, envelope);
|
||||||
|
|
@ -689,7 +695,7 @@ app.get("/v1/projects/:id/documents/:docId", ...requireCoreAuth, requirePermissi
|
||||||
const { doc, envelope } = await rpcDocumentForDownload(
|
const { doc, envelope } = await rpcDocumentForDownload(
|
||||||
db,
|
db,
|
||||||
"core.fn_project_document_get",
|
"core.fn_project_document_get",
|
||||||
{ project_id: projectId, id: docId },
|
{ project_id: projectId, doc_id: docId },
|
||||||
route,
|
route,
|
||||||
);
|
);
|
||||||
if (!doc) return respondRpc(c, envelope);
|
if (!doc) return respondRpc(c, envelope);
|
||||||
|
|
@ -1237,7 +1243,7 @@ app.get("/v1/workers/:id/documents/:docId", ...requireCoreAuth, requirePermissio
|
||||||
const { doc, envelope } = await rpcDocumentForDownload(
|
const { doc, envelope } = await rpcDocumentForDownload(
|
||||||
db,
|
db,
|
||||||
"core.fn_worker_document_get",
|
"core.fn_worker_document_get",
|
||||||
{ worker_id: workerId, id: docId },
|
{ worker_id: workerId, doc_id: docId },
|
||||||
route,
|
route,
|
||||||
);
|
);
|
||||||
if (!doc) return respondRpc(c, envelope);
|
if (!doc) return respondRpc(c, envelope);
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,6 @@
|
||||||
<include file="changesets/029-schema-work-program.sql" relativeToChangelogFile="true"/>
|
<include file="changesets/029-schema-work-program.sql" relativeToChangelogFile="true"/>
|
||||||
<include file="changesets/030-rpc-work-program.sql" relativeToChangelogFile="true"/>
|
<include file="changesets/030-rpc-work-program.sql" relativeToChangelogFile="true"/>
|
||||||
<include file="changesets/031-project-doc-types-technical.sql" relativeToChangelogFile="true"/>
|
<include file="changesets/031-project-doc-types-technical.sql" relativeToChangelogFile="true"/>
|
||||||
|
<include file="changesets/032-rpc-document-get-id.sql" relativeToChangelogFile="true"/>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
|
||||||
99
db/core/changesets/032-rpc-document-get-id.sql
Normal file
99
db/core/changesets/032-rpc-document-get-id.sql
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
--liquibase formatted sql
|
||||||
|
-- PANELS · core · fn_*_document_get acepta doc_id o id
|
||||||
|
-- La API HTTP enviaba { id } y las RPC leían payload->>'doc_id', dejando el id vacío.
|
||||||
|
|
||||||
|
--changeset panel:core-032a-fn-project-document-get splitStatements:false
|
||||||
|
CREATE OR REPLACE FUNCTION core.fn_project_document_get(payload jsonb)
|
||||||
|
RETURNS jsonb
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
SECURITY INVOKER
|
||||||
|
SET search_path = core
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
v_project_id bigint := NULLIF(payload->>'project_id', '')::bigint;
|
||||||
|
v_doc_id bigint := NULLIF(COALESCE(payload->>'doc_id', payload->>'id'), '')::bigint;
|
||||||
|
v_doc jsonb;
|
||||||
|
BEGIN
|
||||||
|
IF v_project_id IS NULL OR v_doc_id IS NULL THEN
|
||||||
|
RETURN core.rpc_err('VALIDATION',
|
||||||
|
'fn_project_document_get: project_id y doc_id son obligatorios',
|
||||||
|
jsonb_build_object('fn', 'fn_project_document_get', 'project_id', v_project_id, 'doc_id', v_doc_id));
|
||||||
|
END IF;
|
||||||
|
SELECT to_jsonb(d) INTO v_doc FROM project_documents d
|
||||||
|
WHERE d.id = v_doc_id AND d.project_id = v_project_id;
|
||||||
|
IF v_doc IS NULL THEN
|
||||||
|
RETURN core.rpc_err('NOT_FOUND',
|
||||||
|
format('fn_project_document_get: documento id=%s no encontrado en proyecto %s', v_doc_id, v_project_id),
|
||||||
|
jsonb_build_object('fn', 'fn_project_document_get', 'doc_id', v_doc_id, 'project_id', v_project_id));
|
||||||
|
END IF;
|
||||||
|
RETURN core.rpc_ok(jsonb_build_object('document', v_doc),
|
||||||
|
format('Documento id=%s del proyecto %s obtenido', v_doc_id, v_project_id),
|
||||||
|
jsonb_build_object('fn', 'fn_project_document_get', 'doc_id', v_doc_id));
|
||||||
|
EXCEPTION WHEN OTHERS THEN
|
||||||
|
RETURN core.rpc_from_exception('fn_project_document_get', SQLSTATE, SQLERRM);
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
--changeset panel:core-032b-fn-company-document-get splitStatements:false
|
||||||
|
CREATE OR REPLACE FUNCTION core.fn_company_document_get(payload jsonb)
|
||||||
|
RETURNS jsonb
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
SECURITY INVOKER
|
||||||
|
SET search_path = core
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
v_company_id bigint := NULLIF(payload->>'company_id', '')::bigint;
|
||||||
|
v_doc_id bigint := NULLIF(COALESCE(payload->>'doc_id', payload->>'id'), '')::bigint;
|
||||||
|
v_doc jsonb;
|
||||||
|
BEGIN
|
||||||
|
IF v_company_id IS NULL OR v_doc_id IS NULL THEN
|
||||||
|
RETURN core.rpc_err('VALIDATION',
|
||||||
|
'fn_company_document_get: company_id y doc_id son obligatorios',
|
||||||
|
jsonb_build_object('fn', 'fn_company_document_get', 'company_id', v_company_id, 'doc_id', v_doc_id));
|
||||||
|
END IF;
|
||||||
|
SELECT to_jsonb(d) INTO v_doc FROM company_documents d
|
||||||
|
WHERE d.id = v_doc_id AND d.company_id = v_company_id;
|
||||||
|
IF v_doc IS NULL THEN
|
||||||
|
RETURN core.rpc_err('NOT_FOUND',
|
||||||
|
format('fn_company_document_get: documento id=%s no encontrado en empresa %s', v_doc_id, v_company_id),
|
||||||
|
jsonb_build_object('fn', 'fn_company_document_get', 'doc_id', v_doc_id, 'company_id', v_company_id));
|
||||||
|
END IF;
|
||||||
|
RETURN core.rpc_ok(jsonb_build_object('document', v_doc),
|
||||||
|
format('Documento id=%s de la empresa %s obtenido', v_doc_id, v_company_id),
|
||||||
|
jsonb_build_object('fn', 'fn_company_document_get', 'doc_id', v_doc_id));
|
||||||
|
EXCEPTION WHEN OTHERS THEN
|
||||||
|
RETURN core.rpc_from_exception('fn_company_document_get', SQLSTATE, SQLERRM);
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
--changeset panel:core-032c-fn-worker-document-get splitStatements:false
|
||||||
|
CREATE OR REPLACE FUNCTION core.fn_worker_document_get(payload jsonb)
|
||||||
|
RETURNS jsonb
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
SECURITY INVOKER
|
||||||
|
SET search_path = core
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
v_worker_id bigint := NULLIF(payload->>'worker_id', '')::bigint;
|
||||||
|
v_doc_id bigint := NULLIF(COALESCE(payload->>'doc_id', payload->>'id'), '')::bigint;
|
||||||
|
v_doc jsonb;
|
||||||
|
BEGIN
|
||||||
|
IF v_worker_id IS NULL OR v_doc_id IS NULL THEN
|
||||||
|
RETURN core.rpc_err('VALIDATION',
|
||||||
|
'fn_worker_document_get: worker_id y doc_id son obligatorios',
|
||||||
|
jsonb_build_object('fn', 'fn_worker_document_get', 'worker_id', v_worker_id, 'doc_id', v_doc_id));
|
||||||
|
END IF;
|
||||||
|
SELECT to_jsonb(d) INTO v_doc FROM documents d
|
||||||
|
WHERE d.id = v_doc_id AND d.worker_id = v_worker_id;
|
||||||
|
IF v_doc IS NULL THEN
|
||||||
|
RETURN core.rpc_err('NOT_FOUND',
|
||||||
|
format('fn_worker_document_get: documento id=%s no encontrado para trabajador %s', v_doc_id, v_worker_id),
|
||||||
|
jsonb_build_object('fn', 'fn_worker_document_get', 'doc_id', v_doc_id, 'worker_id', v_worker_id));
|
||||||
|
END IF;
|
||||||
|
RETURN core.rpc_ok(jsonb_build_object('document', v_doc),
|
||||||
|
format('Documento id=%s del trabajador %s obtenido', v_doc_id, v_worker_id),
|
||||||
|
jsonb_build_object('fn', 'fn_worker_document_get', 'doc_id', v_doc_id));
|
||||||
|
EXCEPTION WHEN OTHERS THEN
|
||||||
|
RETURN core.rpc_from_exception('fn_worker_document_get', SQLSTATE, SQLERRM);
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
Loading…
Reference in a new issue