refactor: remove obsolete member management API module
Some checks failed
Workflow / ʦ TypeScript (pull_request) Failing after 5m57s
Workflow / ⚫️ Test (pull_request) Has been skipped

This commit is contained in:
T. Zehetbauer
2026-04-03 14:08:31 +02:00
parent 124c6a632a
commit 5c5aaabae5
132 changed files with 10107 additions and 3442 deletions

View File

@@ -0,0 +1,150 @@
-- =====================================================
-- SEPA Data Deduplication (Phase 1)
--
-- Problem: members table has inline SEPA fields (iban, bic,
-- account_holder, sepa_mandate_id, sepa_mandate_date,
-- sepa_mandate_status, sepa_mandate_sequence, sepa_bank_name)
-- AND a separate sepa_mandates table. sepa_mandate_id is text,
-- not a FK to sepa_mandates(id) which is uuid. Data diverges.
--
-- Fix: Add proper primary_mandate_id FK, migrate inline data
-- to sepa_mandates rows, rewrite RPCs to read from sepa_mandates.
-- Inline columns are kept read-only for backward compat (phase 2 drops them).
-- =====================================================
-- Step 1: Add proper FK column pointing to the primary mandate
ALTER TABLE public.members
ADD COLUMN IF NOT EXISTS primary_mandate_id uuid
REFERENCES public.sepa_mandates(id) ON DELETE SET NULL;
CREATE INDEX IF NOT EXISTS ix_members_primary_mandate
ON public.members(primary_mandate_id)
WHERE primary_mandate_id IS NOT NULL;
-- Step 2: For members with inline SEPA data but no sepa_mandates row, create one
DO $$
DECLARE
r record;
v_mandate_id uuid;
BEGIN
FOR r IN
SELECT m.id AS member_id, m.account_id,
m.iban, m.bic, m.account_holder,
m.first_name, m.last_name,
m.sepa_mandate_id, m.sepa_mandate_date,
m.sepa_mandate_status, m.sepa_mandate_reference,
m.sepa_mandate_sequence, m.sepa_bank_name
FROM public.members m
WHERE m.iban IS NOT NULL AND m.iban != ''
AND NOT EXISTS (
SELECT 1 FROM public.sepa_mandates sm WHERE sm.member_id = m.id
)
LOOP
INSERT INTO public.sepa_mandates (
member_id, account_id, mandate_reference, iban, bic,
account_holder, mandate_date, status, sequence, is_primary, notes
) VALUES (
r.member_id,
r.account_id,
COALESCE(NULLIF(r.sepa_mandate_reference, ''), NULLIF(r.sepa_mandate_id, ''), 'MIGRATED-' || r.member_id::text),
r.iban,
r.bic,
COALESCE(NULLIF(r.account_holder, ''), NULLIF(TRIM(COALESCE(r.first_name, '') || ' ' || COALESCE(r.last_name, '')), ''), 'Unbekannt'),
COALESCE(r.sepa_mandate_date, current_date),
COALESCE(r.sepa_mandate_status, 'pending'::public.sepa_mandate_status),
COALESCE(NULLIF(r.sepa_mandate_sequence, ''), 'RCUR'),
true,
CASE WHEN r.sepa_bank_name IS NOT NULL AND r.sepa_bank_name != ''
THEN 'Bank: ' || r.sepa_bank_name
ELSE NULL
END
)
RETURNING id INTO v_mandate_id;
UPDATE public.members SET primary_mandate_id = v_mandate_id WHERE id = r.member_id;
END LOOP;
END $$;
-- Step 3: For members that already have sepa_mandates rows, link the primary one
UPDATE public.members m
SET primary_mandate_id = sm.id
FROM public.sepa_mandates sm
WHERE sm.member_id = m.id
AND sm.is_primary = true
AND m.primary_mandate_id IS NULL;
-- If no mandate marked as primary, pick the most recent active one
UPDATE public.members m
SET primary_mandate_id = (
SELECT sm.id FROM public.sepa_mandates sm
WHERE sm.member_id = m.id
ORDER BY
CASE WHEN sm.status = 'active' THEN 0 ELSE 1 END,
sm.created_at DESC
LIMIT 1
)
WHERE m.primary_mandate_id IS NULL
AND EXISTS (SELECT 1 FROM public.sepa_mandates sm WHERE sm.member_id = m.id);
-- Step 4: Rewrite list_hierarchy_sepa_eligible_members to read from sepa_mandates
CREATE OR REPLACE FUNCTION public.list_hierarchy_sepa_eligible_members(
root_account_id uuid,
p_account_filter uuid DEFAULT NULL
)
RETURNS TABLE (
member_id uuid,
account_id uuid,
account_name varchar,
first_name text,
last_name text,
iban text,
bic text,
account_holder text,
mandate_id text,
mandate_date date,
dues_amount numeric
)
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
BEGIN
IF NOT public.has_role_on_account(root_account_id) THEN
RETURN;
END IF;
RETURN QUERY
SELECT
m.id AS member_id,
m.account_id,
a.name AS account_name,
m.first_name,
m.last_name,
sm.iban,
sm.bic,
sm.account_holder,
sm.mandate_reference AS mandate_id,
sm.mandate_date,
COALESCE(dc.amount, 0) AS dues_amount
FROM public.members m
JOIN public.accounts a ON a.id = m.account_id
JOIN public.sepa_mandates sm ON sm.id = m.primary_mandate_id
LEFT JOIN public.dues_categories dc ON dc.id = m.dues_category_id
WHERE m.account_id IN (SELECT d FROM public.get_account_descendants(root_account_id) d)
AND m.status = 'active'
AND sm.iban IS NOT NULL
AND sm.status = 'active'
AND (p_account_filter IS NULL OR m.account_id = p_account_filter)
ORDER BY a.name, m.last_name, m.first_name;
END;
$$;
-- Step 5: Add partial index for fast SEPA-eligible lookups
CREATE INDEX IF NOT EXISTS ix_sepa_mandates_active_primary
ON public.sepa_mandates(member_id)
WHERE status = 'active' AND is_primary = true;
-- Note: Inline SEPA columns (iban, bic, account_holder, sepa_mandate_id,
-- sepa_mandate_date, sepa_mandate_status, sepa_mandate_sequence, sepa_bank_name)
-- are kept for read-only backward compatibility. Phase 2 migration will drop them
-- after all code paths are migrated to use sepa_mandates via primary_mandate_id.