212 lines
8.1 KiB
SQL
212 lines
8.1 KiB
SQL
/*
|
|
* -------------------------------------------------------
|
|
* Member Management Schema
|
|
* Phase 4: members, membership_applications, dues_categories, member_cards
|
|
* -------------------------------------------------------
|
|
*/
|
|
|
|
-- =====================================================
|
|
-- 1. Enums
|
|
-- =====================================================
|
|
create type public.membership_status as enum(
|
|
'active', 'inactive', 'pending', 'resigned', 'excluded', 'deceased'
|
|
);
|
|
|
|
create type public.sepa_mandate_status as enum(
|
|
'active', 'pending', 'revoked', 'expired'
|
|
);
|
|
|
|
create type public.application_status as enum(
|
|
'submitted', 'review', 'approved', 'rejected'
|
|
);
|
|
|
|
-- =====================================================
|
|
-- 2. members
|
|
-- =====================================================
|
|
create table if not exists public.members (
|
|
id uuid primary key default gen_random_uuid(),
|
|
account_id uuid not null references public.accounts(id) on delete cascade,
|
|
member_number text,
|
|
|
|
-- Personal
|
|
first_name text not null,
|
|
last_name text not null,
|
|
date_of_birth date,
|
|
gender text check (gender in ('male', 'female', 'diverse', null)),
|
|
title text, -- Dr., Prof., etc.
|
|
|
|
-- Contact
|
|
email text,
|
|
phone text,
|
|
mobile text,
|
|
|
|
-- Address
|
|
street text,
|
|
house_number text,
|
|
postal_code text,
|
|
city text,
|
|
country text default 'DE',
|
|
|
|
-- Membership
|
|
status public.membership_status not null default 'active',
|
|
entry_date date not null default current_date,
|
|
exit_date date,
|
|
exit_reason text,
|
|
dues_category_id uuid,
|
|
|
|
-- SEPA
|
|
sepa_mandate_id text,
|
|
sepa_mandate_date date,
|
|
sepa_mandate_status public.sepa_mandate_status default 'pending',
|
|
iban text,
|
|
bic text,
|
|
account_holder text,
|
|
|
|
-- GDPR
|
|
gdpr_consent boolean not null default false,
|
|
gdpr_consent_date timestamptz,
|
|
gdpr_data_source text,
|
|
|
|
-- Meta
|
|
notes text,
|
|
custom_data jsonb not null default '{}'::jsonb,
|
|
created_by uuid references auth.users(id) on delete set null,
|
|
updated_by uuid references auth.users(id) on delete set null,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now(),
|
|
|
|
unique(account_id, member_number)
|
|
);
|
|
|
|
comment on table public.members is 'Club/association members — replaces legacy ve_mitglieder';
|
|
|
|
create index ix_members_account on public.members(account_id);
|
|
create index ix_members_status on public.members(account_id, status);
|
|
create index ix_members_name on public.members(account_id, last_name, first_name);
|
|
create index ix_members_email on public.members(account_id, email);
|
|
|
|
alter table public.members enable row level security;
|
|
revoke all on public.members from authenticated, service_role;
|
|
grant select, insert, update, delete on public.members to authenticated;
|
|
grant all on public.members to service_role;
|
|
|
|
create policy members_select on public.members for select to authenticated
|
|
using (public.has_role_on_account(account_id));
|
|
|
|
create policy members_insert on public.members for insert to authenticated
|
|
with check (public.has_permission(auth.uid(), account_id, 'members.write'::public.app_permissions));
|
|
|
|
create policy members_update on public.members for update to authenticated
|
|
using (public.has_permission(auth.uid(), account_id, 'members.write'::public.app_permissions));
|
|
|
|
create policy members_delete on public.members for delete to authenticated
|
|
using (public.has_permission(auth.uid(), account_id, 'members.write'::public.app_permissions));
|
|
|
|
create trigger trg_members_updated_at
|
|
before update on public.members
|
|
for each row execute function public.update_account_settings_timestamp();
|
|
|
|
-- =====================================================
|
|
-- 3. dues_categories — tiered pricing
|
|
-- =====================================================
|
|
create table if not exists public.dues_categories (
|
|
id uuid primary key default gen_random_uuid(),
|
|
account_id uuid not null references public.accounts(id) on delete cascade,
|
|
name text not null,
|
|
description text,
|
|
amount numeric(10,2) not null default 0,
|
|
interval text not null default 'yearly' check (interval in ('monthly', 'quarterly', 'half_yearly', 'yearly')),
|
|
is_default boolean not null default false,
|
|
sort_order integer not null default 0,
|
|
created_at timestamptz not null default now()
|
|
);
|
|
|
|
comment on table public.dues_categories is 'Membership dues/fee categories';
|
|
|
|
create index ix_dues_categories_account on public.dues_categories(account_id);
|
|
|
|
alter table public.dues_categories enable row level security;
|
|
revoke all on public.dues_categories from authenticated, service_role;
|
|
grant select, insert, update, delete on public.dues_categories to authenticated;
|
|
grant all on public.dues_categories to service_role;
|
|
|
|
create policy dues_categories_select on public.dues_categories for select to authenticated
|
|
using (public.has_role_on_account(account_id));
|
|
|
|
create policy dues_categories_mutate on public.dues_categories for all to authenticated
|
|
using (public.has_permission(auth.uid(), account_id, 'members.write'::public.app_permissions));
|
|
|
|
-- Add FK from members to dues_categories
|
|
alter table public.members
|
|
add constraint fk_members_dues_category
|
|
foreign key (dues_category_id) references public.dues_categories(id) on delete set null;
|
|
|
|
-- =====================================================
|
|
-- 4. membership_applications — workflow
|
|
-- =====================================================
|
|
create table if not exists public.membership_applications (
|
|
id uuid primary key default gen_random_uuid(),
|
|
account_id uuid not null references public.accounts(id) on delete cascade,
|
|
first_name text not null,
|
|
last_name text not null,
|
|
email text,
|
|
phone text,
|
|
street text,
|
|
postal_code text,
|
|
city text,
|
|
date_of_birth date,
|
|
message text,
|
|
status public.application_status not null default 'submitted',
|
|
reviewed_by uuid references auth.users(id) on delete set null,
|
|
reviewed_at timestamptz,
|
|
review_notes text,
|
|
member_id uuid references public.members(id) on delete set null,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now()
|
|
);
|
|
|
|
comment on table public.membership_applications is 'Online membership applications with approval workflow';
|
|
|
|
create index ix_applications_account on public.membership_applications(account_id);
|
|
create index ix_applications_status on public.membership_applications(account_id, status);
|
|
|
|
alter table public.membership_applications enable row level security;
|
|
revoke all on public.membership_applications from authenticated, service_role;
|
|
grant select, insert, update, delete on public.membership_applications to authenticated;
|
|
grant all on public.membership_applications to service_role;
|
|
|
|
create policy applications_select on public.membership_applications for select to authenticated
|
|
using (public.has_role_on_account(account_id));
|
|
|
|
create policy applications_mutate on public.membership_applications for all to authenticated
|
|
using (public.has_permission(auth.uid(), account_id, 'members.write'::public.app_permissions));
|
|
|
|
-- =====================================================
|
|
-- 5. member_cards — ID cards
|
|
-- =====================================================
|
|
create table if not exists public.member_cards (
|
|
id uuid primary key default gen_random_uuid(),
|
|
member_id uuid not null references public.members(id) on delete cascade,
|
|
account_id uuid not null references public.accounts(id) on delete cascade,
|
|
card_number text,
|
|
valid_from date not null default current_date,
|
|
valid_until date,
|
|
pdf_storage_path text,
|
|
created_at timestamptz not null default now()
|
|
);
|
|
|
|
comment on table public.member_cards is 'Member ID cards with generated PDFs';
|
|
|
|
create index ix_member_cards_member on public.member_cards(member_id);
|
|
|
|
alter table public.member_cards enable row level security;
|
|
revoke all on public.member_cards from authenticated, service_role;
|
|
grant select, insert, update, delete on public.member_cards to authenticated;
|
|
grant all on public.member_cards to service_role;
|
|
|
|
create policy member_cards_select on public.member_cards for select to authenticated
|
|
using (public.has_role_on_account(account_id));
|
|
|
|
create policy member_cards_mutate on public.member_cards for all to authenticated
|
|
using (public.has_permission(auth.uid(), account_id, 'members.write'::public.app_permissions));
|