mirror of
https://origin.cursor.com/mrdevmx/panels.git
synced 2026-10-09 20:23:17 +00:00
Si un gasto se guardó sin clasificar, ahora se edita desde la lista (clic o Reclasificar) y se le asigna o cambia el concepto del presupuesto. Co-authored-by: alberto.martinez <alberto.martinez@mrdev.mx>
111 lines
5.2 KiB
PL/PgSQL
111 lines
5.2 KiB
PL/PgSQL
--liquibase formatted sql
|
|
-- PANELS · core · reclasificar gasto (concepto, proveedor, obra)
|
|
|
|
--changeset panel:core-034a-fn-expense-update-reclassify splitStatements:false
|
|
CREATE OR REPLACE FUNCTION core.fn_expense_update(payload jsonb)
|
|
RETURNS jsonb
|
|
LANGUAGE plpgsql
|
|
SECURITY INVOKER
|
|
SET search_path = core
|
|
AS $$
|
|
DECLARE
|
|
v_id bigint := NULLIF(payload->>'id', '')::bigint;
|
|
v_tid integer := core._cost_tenant_id(payload);
|
|
v_cur expense_entries%ROWTYPE;
|
|
v_warning jsonb;
|
|
v_row jsonb;
|
|
v_subtotal numeric; v_iva_amount numeric; v_total numeric;
|
|
v_line numeric;
|
|
v_supplier_id bigint;
|
|
v_supplier_name text;
|
|
v_supplier_rfc text;
|
|
v_budget_item_id bigint;
|
|
v_chapter_id bigint;
|
|
BEGIN
|
|
IF v_id IS NULL THEN
|
|
RETURN core.rpc_err('VALIDATION', 'fn_expense_update: id es obligatorio',
|
|
jsonb_build_object('fn', 'fn_expense_update', 'field', 'id'));
|
|
END IF;
|
|
SELECT * INTO v_cur FROM expense_entries WHERE id = v_id;
|
|
IF NOT FOUND THEN
|
|
RETURN core.rpc_err('NOT_FOUND', format('fn_expense_update: gasto id=%s no encontrado', v_id),
|
|
jsonb_build_object('fn', 'fn_expense_update', 'id', v_id));
|
|
END IF;
|
|
IF v_cur.is_locked OR v_cur.source <> 'manual' THEN
|
|
RETURN core.rpc_err('VALIDATION', 'fn_expense_update: solo se editan gastos manuales no bloqueados',
|
|
jsonb_build_object('fn', 'fn_expense_update', 'id', v_id, 'source', v_cur.source));
|
|
END IF;
|
|
|
|
IF payload ? 'supplier_id' THEN
|
|
v_supplier_id := NULLIF(payload->>'supplier_id', '')::bigint;
|
|
IF v_supplier_id IS NOT NULL THEN
|
|
SELECT name, rfc INTO v_supplier_name, v_supplier_rfc
|
|
FROM suppliers
|
|
WHERE id = v_supplier_id AND tenant_id = v_tid AND status = 'activo';
|
|
IF NOT FOUND THEN
|
|
RETURN core.rpc_err('NOT_FOUND', format('fn_expense_update: proveedor id=%s no encontrado', v_supplier_id),
|
|
jsonb_build_object('fn', 'fn_expense_update', 'supplier_id', v_supplier_id));
|
|
END IF;
|
|
ELSE
|
|
v_supplier_name := '';
|
|
v_supplier_rfc := '';
|
|
END IF;
|
|
END IF;
|
|
|
|
IF payload ? 'budget_item_id' THEN
|
|
v_budget_item_id := NULLIF(payload->>'budget_item_id', '')::bigint;
|
|
IF v_budget_item_id IS NOT NULL THEN
|
|
SELECT chapter_id INTO v_chapter_id FROM budget_items WHERE id = v_budget_item_id;
|
|
END IF;
|
|
END IF;
|
|
|
|
UPDATE expense_entries SET
|
|
expense_date = COALESCE(NULLIF(payload->>'expense_date', '')::date, expense_date),
|
|
description = COALESCE(nullif(btrim(payload->>'description'), ''), description),
|
|
project_id = CASE WHEN payload ? 'project_id' THEN NULLIF(payload->>'project_id', '')::bigint ELSE project_id END,
|
|
supplier_id = CASE WHEN payload ? 'supplier_id' THEN v_supplier_id ELSE supplier_id END,
|
|
supplier_name = CASE
|
|
WHEN payload ? 'supplier_id' THEN coalesce(v_supplier_name, '')
|
|
WHEN payload ? 'supplier_name' THEN coalesce(nullif(btrim(payload->>'supplier_name'), ''), '')
|
|
ELSE supplier_name END,
|
|
supplier_rfc = CASE
|
|
WHEN payload ? 'supplier_id' THEN coalesce(v_supplier_rfc, '')
|
|
WHEN payload ? 'supplier_rfc' THEN upper(coalesce(nullif(btrim(payload->>'supplier_rfc'), ''), ''))
|
|
ELSE supplier_rfc END,
|
|
budget_item_id = CASE WHEN payload ? 'budget_item_id' THEN v_budget_item_id ELSE budget_item_id END,
|
|
budget_chapter_id = CASE WHEN payload ? 'budget_item_id' THEN v_chapter_id ELSE budget_chapter_id END,
|
|
quantity = CASE WHEN payload ? 'quantity' THEN COALESCE((payload->>'quantity')::numeric, 0) ELSE quantity END,
|
|
unit = CASE WHEN payload ? 'unit' THEN coalesce(nullif(btrim(payload->>'unit'), ''), '') ELSE unit END,
|
|
unit_price = CASE WHEN payload ? 'unit_price' THEN COALESCE((payload->>'unit_price')::numeric, 0) ELSE unit_price END,
|
|
tax_included = CASE WHEN payload ? 'tax_included' THEN COALESCE((payload->>'tax_included')::boolean, false) ELSE tax_included END,
|
|
notes = CASE WHEN payload ? 'notes' THEN coalesce(nullif(btrim(payload->>'notes'), ''), '') ELSE notes END
|
|
WHERE id = v_id;
|
|
SELECT * INTO v_cur FROM expense_entries WHERE id = v_id;
|
|
v_line := CASE
|
|
WHEN v_cur.quantity > 0 AND v_cur.unit_price > 0 THEN round(v_cur.quantity * v_cur.unit_price, 2)
|
|
ELSE v_cur.subtotal
|
|
END;
|
|
SELECT c.subtotal, c.iva_amount, c.total INTO v_subtotal, v_iva_amount, v_total
|
|
FROM core._cost_calc_amounts(
|
|
v_line,
|
|
v_cur.iva_rate,
|
|
v_cur.tax_included,
|
|
CASE WHEN v_cur.tax_included THEN v_line ELSE NULL END
|
|
) c;
|
|
UPDATE expense_entries SET subtotal = v_subtotal, iva_amount = v_iva_amount, total = v_total WHERE id = v_id;
|
|
IF v_cur.counts_toward_budget AND v_cur.budget_item_id IS NOT NULL THEN
|
|
v_warning := core._cost_budget_warning(v_tid, v_cur.budget_item_id, 0, 0);
|
|
END IF;
|
|
SELECT to_jsonb(e) INTO v_row FROM expense_entries e WHERE id = v_id;
|
|
RETURN core.rpc_ok(
|
|
jsonb_build_object('expense', v_row, 'budget_warning', v_warning),
|
|
format('Gasto id=%s actualizado', v_id),
|
|
jsonb_build_object('fn', 'fn_expense_update', 'id', v_id, 'budget_item_id', v_cur.budget_item_id)
|
|
);
|
|
EXCEPTION WHEN OTHERS THEN
|
|
RETURN core.rpc_from_exception('fn_expense_update', SQLSTATE, SQLERRM);
|
|
END;
|
|
$$;
|
|
|
|
--changeset panel:core-034b-fn-expense-update-grant endDelimiter:; splitStatements:true
|
|
GRANT EXECUTE ON FUNCTION core.fn_expense_update(jsonb) TO panels_core_app;
|