* fix: fix SIE import voucher range bugs from multi-tenant migration and remove describe transaction feature - Fix reserve_voucher_range RPC parameter mismatch (p_user_id → p_company_id) causing duplicate voucher errors - Fix create-accounts onConflict from user_id to company_id - Add reserve-then-adjust pattern: pre-reserve voucher range before batch insert, release unused range on partial failure - Add release_voucher_range DB function for safe rollback - Remove DescribeTransactionDialog, describe/batch-describe API routes, and related schemas (feature superseded) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Greptile review — voucher range race condition and orphaned entries - Add p_reserved_highest upper-bound guard to release_voucher_range to prevent rolling back past numbers claimed by concurrent operations - Move highestInsertedVoucher tracking to after both headers AND lines succeed, preventing orphaned journal entries with no lines from being counted as "used" Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
70 lines
2.7 KiB
PL/PgSQL
70 lines
2.7 KiB
PL/PgSQL
-- Fix reserve_voucher_range: p_user_id was not updated to p_company_id
|
|
-- during the multi-tenant migration (20260330130000). The parameter mismatch
|
|
-- caused SIE imports to silently fail to update the voucher sequence,
|
|
-- leading to duplicate voucher number errors on subsequent operations.
|
|
--
|
|
-- Also adds release_voucher_range for rolling back burned numbers on partial
|
|
-- import failure (reserve-then-adjust pattern).
|
|
|
|
-- =============================================================================
|
|
-- 1. Fix reserve_voucher_range: p_user_id -> p_company_id
|
|
-- =============================================================================
|
|
DROP FUNCTION IF EXISTS public.reserve_voucher_range(uuid, uuid, text, integer);
|
|
|
|
CREATE OR REPLACE FUNCTION public.reserve_voucher_range(
|
|
p_company_id uuid,
|
|
p_fiscal_period_id uuid,
|
|
p_series text,
|
|
p_highest_used integer
|
|
)
|
|
RETURNS void
|
|
LANGUAGE plpgsql
|
|
SECURITY DEFINER
|
|
SET search_path = public
|
|
AS $$
|
|
BEGIN
|
|
INSERT INTO public.voucher_sequences (company_id, user_id, fiscal_period_id, voucher_series, last_number)
|
|
VALUES (p_company_id, auth.uid(), p_fiscal_period_id, p_series, p_highest_used)
|
|
ON CONFLICT (company_id, fiscal_period_id, voucher_series)
|
|
DO UPDATE SET
|
|
last_number = GREATEST(public.voucher_sequences.last_number, EXCLUDED.last_number),
|
|
updated_at = now();
|
|
END;
|
|
$$;
|
|
|
|
GRANT EXECUTE ON FUNCTION public.reserve_voucher_range(uuid, uuid, text, integer) TO authenticated;
|
|
|
|
-- =============================================================================
|
|
-- 2. release_voucher_range: roll back sequence on partial import failure
|
|
-- Only decreases last_number (never increases), preventing race conditions
|
|
-- with concurrent operations that may have legitimately advanced the sequence.
|
|
-- =============================================================================
|
|
CREATE OR REPLACE FUNCTION public.release_voucher_range(
|
|
p_company_id uuid,
|
|
p_fiscal_period_id uuid,
|
|
p_series text,
|
|
p_actual_last integer,
|
|
p_reserved_highest integer -- the ceiling this import originally reserved
|
|
)
|
|
RETURNS void
|
|
LANGUAGE plpgsql
|
|
SECURITY DEFINER
|
|
SET search_path = public
|
|
AS $$
|
|
BEGIN
|
|
-- Only release within the range this import originally reserved.
|
|
-- The upper-bound guard (last_number <= p_reserved_highest) prevents rolling
|
|
-- back past numbers that a concurrent operation has legitimately claimed.
|
|
UPDATE public.voucher_sequences
|
|
SET last_number = p_actual_last,
|
|
updated_at = now()
|
|
WHERE company_id = p_company_id
|
|
AND fiscal_period_id = p_fiscal_period_id
|
|
AND voucher_series = p_series
|
|
AND last_number > p_actual_last
|
|
AND last_number <= p_reserved_highest;
|
|
END;
|
|
$$;
|
|
|
|
GRANT EXECUTE ON FUNCTION public.release_voucher_range(uuid, uuid, text, integer, integer) TO authenticated;
|