129664533c
* feat: enhance journal entry handling with follow-up entries and related RPC * fix: improve validation for journal entry lines to ensure proper submission criteria * fix: enhance OAuth error handling and user feedback in Arcim migration process * fix: add OAuth error translation for user-friendly feedback in Fortnox integration
126 lines
4.3 KiB
PL/PgSQL
126 lines
4.3 KiB
PL/PgSQL
-- RPC: list journal entries for a fiscal period, optionally including
|
|
-- follow-up entries booked in a later period that relate to aggregates
|
|
-- that originated in the selected period.
|
|
--
|
|
-- Why this exists: journal_entries.fiscal_period_id is strictly bound to
|
|
-- entry_date (validated in lib/bookkeeping/engine.ts). So a customer
|
|
-- invoice created in FY2025 and paid in FY2026 produces two entries in
|
|
-- two different periods, and filtering the /bookkeeping view by
|
|
-- fiscal_period_id hides the tail of the story. Users reviewing a past
|
|
-- fiscal year expect to see the full processing history for that year's
|
|
-- aggregates (behandlingshistorik per BFL/BFNAR).
|
|
--
|
|
-- Expansion rules (when p_include_related = true):
|
|
-- - Entries with source_type in invoice follow-ups whose invoice was
|
|
-- dated inside the selected period.
|
|
-- - Same for supplier invoice follow-ups.
|
|
--
|
|
-- Storno and correction entries inherit fiscal_period_id from their
|
|
-- original (see lib/bookkeeping/engine.ts reverseEntry and
|
|
-- lib/core/bookkeeping/storno-service.ts), so they are already captured
|
|
-- by the primary fiscal_period_id filter — no extra rule needed.
|
|
--
|
|
-- Currency revaluation is booked within the period it revalues, also
|
|
-- captured by the primary filter.
|
|
--
|
|
-- Returns jsonb rows shaped like the PostgREST response used by
|
|
-- GET /api/bookkeeping/journal-entries (entry + nested lines), plus an
|
|
-- out_of_period boolean the UI uses to badge tail entries.
|
|
|
|
CREATE OR REPLACE FUNCTION public.list_fiscal_period_entries_with_related(
|
|
p_company_id uuid,
|
|
p_period_id uuid,
|
|
p_include_related boolean DEFAULT true,
|
|
p_status text DEFAULT NULL,
|
|
p_date_from date DEFAULT NULL,
|
|
p_date_to date DEFAULT NULL,
|
|
p_sort_date text DEFAULT 'desc',
|
|
p_limit int DEFAULT 50,
|
|
p_offset int DEFAULT 0
|
|
)
|
|
RETURNS TABLE (
|
|
entry jsonb,
|
|
total_count bigint
|
|
)
|
|
LANGUAGE sql
|
|
STABLE
|
|
SECURITY INVOKER
|
|
SET search_path = public, pg_temp
|
|
AS $$
|
|
WITH period AS (
|
|
SELECT period_start, period_end
|
|
FROM public.fiscal_periods
|
|
WHERE id = p_period_id AND company_id = p_company_id
|
|
),
|
|
matching AS (
|
|
SELECT je.*
|
|
FROM public.journal_entries je
|
|
CROSS JOIN period p
|
|
WHERE je.company_id = p_company_id
|
|
AND (
|
|
je.fiscal_period_id = p_period_id
|
|
OR (
|
|
p_include_related
|
|
AND je.source_type IN ('invoice_paid','invoice_cash_payment','credit_note')
|
|
AND EXISTS (
|
|
SELECT 1 FROM public.invoices i
|
|
WHERE i.id = je.source_id
|
|
AND i.company_id = p_company_id
|
|
AND i.invoice_date BETWEEN p.period_start AND p.period_end
|
|
)
|
|
)
|
|
OR (
|
|
p_include_related
|
|
AND je.source_type IN ('supplier_invoice_paid','supplier_invoice_cash_payment','supplier_credit_note')
|
|
AND EXISTS (
|
|
SELECT 1 FROM public.supplier_invoices si
|
|
WHERE si.id = je.source_id
|
|
AND si.company_id = p_company_id
|
|
AND si.invoice_date BETWEEN p.period_start AND p.period_end
|
|
)
|
|
)
|
|
)
|
|
AND (p_status IS NULL OR je.status = p_status)
|
|
AND (p_date_from IS NULL OR je.entry_date >= p_date_from)
|
|
AND (p_date_to IS NULL OR je.entry_date <= p_date_to)
|
|
),
|
|
matching_with_total AS (
|
|
SELECT m.*, COUNT(*) OVER () AS total
|
|
FROM matching m
|
|
),
|
|
paged AS (
|
|
SELECT *
|
|
FROM matching_with_total
|
|
ORDER BY
|
|
CASE WHEN p_sort_date = 'asc' THEN entry_date END ASC NULLS LAST,
|
|
CASE WHEN p_sort_date = 'desc' THEN entry_date END DESC NULLS LAST,
|
|
voucher_series,
|
|
voucher_number
|
|
LIMIT p_limit OFFSET p_offset
|
|
)
|
|
SELECT
|
|
(to_jsonb(p.*) - 'total')
|
|
|| jsonb_build_object(
|
|
'lines', COALESCE(
|
|
(SELECT jsonb_agg(to_jsonb(l.*) ORDER BY l.sort_order)
|
|
FROM public.journal_entry_lines l
|
|
WHERE l.journal_entry_id = p.id),
|
|
'[]'::jsonb
|
|
),
|
|
'out_of_period', (p.fiscal_period_id IS DISTINCT FROM p_period_id)
|
|
) AS entry,
|
|
p.total AS total_count
|
|
FROM paged p
|
|
ORDER BY
|
|
CASE WHEN p_sort_date = 'asc' THEN p.entry_date END ASC NULLS LAST,
|
|
CASE WHEN p_sort_date = 'desc' THEN p.entry_date END DESC NULLS LAST,
|
|
p.voucher_series,
|
|
p.voucher_number;
|
|
$$;
|
|
|
|
GRANT EXECUTE ON FUNCTION public.list_fiscal_period_entries_with_related(
|
|
uuid, uuid, boolean, text, date, date, text, int, int
|
|
) TO authenticated;
|
|
|
|
NOTIFY pgrst, 'reload schema';
|