implementierung der ersten schritte eine komplett-suite

This commit is contained in:
Torsten Schulz (local)
2026-06-19 15:47:32 +02:00
parent 111b37b287
commit 542fae089c
62 changed files with 11924 additions and 669 deletions

View File

@@ -0,0 +1,202 @@
ALTER TABLE `clubs`
ADD COLUMN IF NOT EXISTS `short_name` varchar(120) NULL,
ADD COLUMN IF NOT EXISTS `legal_name` varchar(255) NULL,
ADD COLUMN IF NOT EXISTS `club_number` varchar(64) NULL,
ADD COLUMN IF NOT EXISTS `email` varchar(255) NULL,
ADD COLUMN IF NOT EXISTS `phone` varchar(80) NULL,
ADD COLUMN IF NOT EXISTS `website` varchar(255) NULL,
ADD COLUMN IF NOT EXISTS `street` varchar(255) NULL,
ADD COLUMN IF NOT EXISTS `postal_code` varchar(24) NULL,
ADD COLUMN IF NOT EXISTS `city` varchar(120) NULL,
ADD COLUMN IF NOT EXISTS `country_code` varchar(2) NOT NULL DEFAULT 'DE',
ADD COLUMN IF NOT EXISTS `chairperson_name` varchar(255) NULL,
ADD COLUMN IF NOT EXISTS `treasurer_name` varchar(255) NULL,
ADD COLUMN IF NOT EXISTS `youth_manager_name` varchar(255) NULL,
ADD COLUMN IF NOT EXISTS `creditor_identifier` varchar(64) NULL,
ADD COLUMN IF NOT EXISTS `billing_email` varchar(255) NULL,
ADD COLUMN IF NOT EXISTS `iban` varchar(34) NULL,
ADD COLUMN IF NOT EXISTS `bic` varchar(11) NULL,
ADD COLUMN IF NOT EXISTS `is_archived` tinyint(1) NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS `archived_at` datetime NULL;
ALTER TABLE `member`
ADD COLUMN IF NOT EXISTS `member_number` varchar(64) NULL,
ADD COLUMN IF NOT EXISTS `membership_status` varchar(32) NOT NULL DEFAULT 'active',
ADD COLUMN IF NOT EXISTS `membership_type` varchar(32) NOT NULL DEFAULT 'regular',
ADD COLUMN IF NOT EXISTS `joined_on` date NULL,
ADD COLUMN IF NOT EXISTS `left_on` date NULL,
ADD COLUMN IF NOT EXISTS `contribution_group_code` varchar(64) NULL,
ADD COLUMN IF NOT EXISTS `needs_sepa_mandate` tinyint(1) NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS `sepa_mandate_reference` varchar(80) NULL,
ADD COLUMN IF NOT EXISTS `is_archived` tinyint(1) NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS `archived_at` datetime NULL,
ADD COLUMN IF NOT EXISTS `archived_reason` text NULL;
CREATE TABLE IF NOT EXISTS `club_requests` (
`id` bigint NOT NULL AUTO_INCREMENT,
`club_id` bigint NOT NULL,
`request_type` varchar(32) NOT NULL,
`status` varchar(32) NOT NULL DEFAULT 'open',
`workflow_stage` varchar(64) NULL,
`priority` varchar(16) NOT NULL DEFAULT 'normal',
`subject` varchar(255) NULL,
`first_name` varchar(120) NULL,
`last_name` varchar(120) NULL,
`email` varchar(255) NULL,
`phone` varchar(80) NULL,
`message` text NULL,
`source_system` varchar(64) NULL,
`received_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`assigned_user_id` bigint NULL,
`assigned_member_id` bigint NULL,
`converted_member_id` bigint NULL,
`closed_at` datetime NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_club_requests_club_status` (`club_id`, `status`, `request_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `club_request_notes` (
`id` bigint NOT NULL AUTO_INCREMENT,
`club_request_id` bigint NOT NULL,
`note_type` varchar(32) NOT NULL DEFAULT 'internal',
`body` text NOT NULL,
`created_by_user_id` bigint NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_club_request_notes_request` (`club_request_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `club_tasks` (
`id` bigint NOT NULL AUTO_INCREMENT,
`club_id` bigint NOT NULL,
`title` varchar(255) NOT NULL,
`task_type` varchar(64) NULL,
`description` text NULL,
`status` varchar(32) NOT NULL DEFAULT 'open',
`priority` varchar(16) NOT NULL DEFAULT 'normal',
`due_at` datetime NULL,
`remind_at` datetime NULL,
`created_by_user_id` bigint NULL,
`assigned_user_id` bigint NULL,
`automation_source` varchar(64) NULL,
`automation_key` varchar(255) NULL,
`related_entity_type` varchar(32) NULL,
`related_entity_id` bigint NULL,
`completed_at` datetime NULL,
`archived_at` datetime NULL,
`source_snapshot` json NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_club_tasks_club_status_due` (`club_id`, `status`, `due_at`),
UNIQUE KEY `uq_club_tasks_automation_key` (`club_id`, `automation_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `club_task_suppressions` (
`id` bigint NOT NULL AUTO_INCREMENT,
`club_id` bigint NOT NULL,
`automation_key` varchar(255) NOT NULL,
`suppression_token` varchar(255) NOT NULL,
`dismissed_by_user_id` bigint NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_club_task_suppressions_key` (`club_id`, `automation_key`),
KEY `idx_club_task_suppressions_lookup` (`club_id`, `automation_key`, `suppression_token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `club_roles` (
`id` bigint NOT NULL AUTO_INCREMENT,
`club_id` bigint NOT NULL,
`role_key` varchar(64) NOT NULL,
`name` varchar(120) NOT NULL,
`description` text NULL,
`permissions` json NOT NULL,
`is_system_role` tinyint(1) NOT NULL DEFAULT 0,
`sort_order` int NOT NULL DEFAULT 0,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_club_roles_key` (`club_id`, `role_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `club_user_roles` (
`id` bigint NOT NULL AUTO_INCREMENT,
`club_id` bigint NOT NULL,
`user_id` bigint NOT NULL,
`club_role_id` bigint NOT NULL,
`is_primary` tinyint(1) NOT NULL DEFAULT 0,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_club_user_roles_assignment` (`club_id`, `user_id`, `club_role_id`),
KEY `idx_club_user_roles_user` (`club_id`, `user_id`),
KEY `idx_club_user_roles_role` (`club_role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `club_sepa_mandates` (
`id` bigint NOT NULL AUTO_INCREMENT,
`club_id` bigint NOT NULL,
`member_id` bigint NULL,
`debtor_name` varchar(255) NOT NULL,
`iban` varchar(34) NOT NULL,
`bic` varchar(11) NULL,
`mandate_reference` varchar(80) NOT NULL,
`signed_on` date NULL,
`valid_from` date NULL,
`revoked_at` datetime NULL,
`status` varchar(32) NOT NULL DEFAULT 'active',
`history_note` text NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_club_sepa_mandates_club_member` (`club_id`, `member_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `club_payment_claims` (
`id` bigint NOT NULL AUTO_INCREMENT,
`club_id` bigint NOT NULL,
`member_id` bigint NULL,
`fee_rule_id` bigint NULL,
`claim_type` varchar(32) NOT NULL DEFAULT 'membership_fee',
`status` varchar(32) NOT NULL DEFAULT 'open',
`due_on` date NOT NULL,
`amount_cents` bigint NOT NULL,
`currency_code` varchar(3) NOT NULL DEFAULT 'EUR',
`reminder_level` int NOT NULL DEFAULT 0,
`last_reminder_at` datetime NULL,
`notes` text NULL,
`settled_at` datetime NULL,
`archived_at` datetime NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_club_payment_claims_club_status_due` (`club_id`, `status`, `due_on`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `club_accounts` (
`id` bigint NOT NULL AUTO_INCREMENT,
`club_id` bigint NOT NULL,
`name` varchar(160) NOT NULL,
`account_holder` varchar(255) NULL,
`bank_name` varchar(160) NULL,
`iban` varchar(34) NULL,
`bic` varchar(11) NULL,
`account_type` varchar(16) NOT NULL DEFAULT 'bank',
`usage_type` varchar(32) NOT NULL DEFAULT 'general',
`currency_code` varchar(3) NOT NULL DEFAULT 'EUR',
`allow_sepa_collections` tinyint(1) NOT NULL DEFAULT 0,
`allow_outgoing_payments` tinyint(1) NOT NULL DEFAULT 1,
`is_default` tinyint(1) NOT NULL DEFAULT 0,
`status` varchar(16) NOT NULL DEFAULT 'active',
`notes` text NULL,
`sort_order` int NOT NULL DEFAULT 0,
`archived_at` datetime NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_club_accounts_club_status` (`club_id`, `status`, `account_type`),
KEY `idx_club_accounts_default` (`club_id`, `is_default`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@@ -0,0 +1,650 @@
BEGIN;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
ALTER TABLE IF EXISTS clubs
ADD COLUMN IF NOT EXISTS short_name varchar(120),
ADD COLUMN IF NOT EXISTS legal_name varchar(255),
ADD COLUMN IF NOT EXISTS club_number varchar(64),
ADD COLUMN IF NOT EXISTS email varchar(255),
ADD COLUMN IF NOT EXISTS phone varchar(80),
ADD COLUMN IF NOT EXISTS website varchar(255),
ADD COLUMN IF NOT EXISTS street varchar(255),
ADD COLUMN IF NOT EXISTS postal_code varchar(24),
ADD COLUMN IF NOT EXISTS city varchar(120),
ADD COLUMN IF NOT EXISTS country_code varchar(2) DEFAULT 'DE',
ADD COLUMN IF NOT EXISTS chairperson_name varchar(255),
ADD COLUMN IF NOT EXISTS treasurer_name varchar(255),
ADD COLUMN IF NOT EXISTS youth_manager_name varchar(255),
ADD COLUMN IF NOT EXISTS creditor_identifier varchar(64),
ADD COLUMN IF NOT EXISTS billing_email varchar(255),
ADD COLUMN IF NOT EXISTS iban varchar(34),
ADD COLUMN IF NOT EXISTS bic varchar(11),
ADD COLUMN IF NOT EXISTS is_archived boolean NOT NULL DEFAULT false,
ADD COLUMN IF NOT EXISTS archived_at timestamptz;
ALTER TABLE IF EXISTS member
ADD COLUMN IF NOT EXISTS member_number varchar(64),
ADD COLUMN IF NOT EXISTS membership_status varchar(32) NOT NULL DEFAULT 'active',
ADD COLUMN IF NOT EXISTS membership_type varchar(32) NOT NULL DEFAULT 'regular',
ADD COLUMN IF NOT EXISTS joined_on date,
ADD COLUMN IF NOT EXISTS left_on date,
ADD COLUMN IF NOT EXISTS contribution_group_code varchar(64),
ADD COLUMN IF NOT EXISTS needs_sepa_mandate boolean NOT NULL DEFAULT false,
ADD COLUMN IF NOT EXISTS sepa_mandate_reference varchar(80),
ADD COLUMN IF NOT EXISTS is_archived boolean NOT NULL DEFAULT false,
ADD COLUMN IF NOT EXISTS archived_at timestamptz,
ADD COLUMN IF NOT EXISTS archived_reason text;
CREATE TABLE IF NOT EXISTS club_requests (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
request_type varchar(32) NOT NULL,
status varchar(32) NOT NULL DEFAULT 'open',
workflow_stage varchar(64),
priority varchar(16) NOT NULL DEFAULT 'normal',
source_system varchar(64),
source_reference varchar(255),
subject varchar(255),
first_name varchar(120),
last_name varchar(120),
email varchar(255),
phone varchar(80),
birthdate date,
message text,
requested_membership_type varchar(32),
assigned_user_id bigint,
assigned_member_id bigint,
converted_member_id bigint,
received_at timestamptz NOT NULL DEFAULT now(),
closed_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_requests_club_status
ON club_requests (club_id, status, request_type);
CREATE TABLE IF NOT EXISTS club_request_notes (
id bigserial PRIMARY KEY,
club_request_id bigint NOT NULL,
note_type varchar(32) NOT NULL DEFAULT 'internal',
body text NOT NULL,
created_by_user_id bigint,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_request_notes_request
ON club_request_notes (club_request_id, created_at DESC);
CREATE TABLE IF NOT EXISTS club_distribution_groups (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
name varchar(160) NOT NULL,
description text,
group_type varchar(32) NOT NULL DEFAULT 'manual',
is_system_group boolean NOT NULL DEFAULT false,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX IF NOT EXISTS uq_club_distribution_groups_name
ON club_distribution_groups (club_id, lower(name));
CREATE TABLE IF NOT EXISTS club_distribution_group_members (
id bigserial PRIMARY KEY,
distribution_group_id bigint NOT NULL,
member_id bigint,
user_id bigint,
email varchar(255),
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_distribution_group_members_group
ON club_distribution_group_members (distribution_group_id);
CREATE TABLE IF NOT EXISTS club_communication_threads (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
thread_type varchar(32) NOT NULL,
subject varchar(255) NOT NULL,
status varchar(32) NOT NULL DEFAULT 'draft',
created_by_user_id bigint,
scheduled_at timestamptz,
sent_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_communication_threads_club_status
ON club_communication_threads (club_id, status, thread_type);
CREATE TABLE IF NOT EXISTS club_communication_recipients (
id bigserial PRIMARY KEY,
thread_id bigint NOT NULL,
member_id bigint,
user_id bigint,
distribution_group_id bigint,
email varchar(255),
delivery_status varchar(32) NOT NULL DEFAULT 'pending',
delivered_at timestamptz,
read_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_communication_recipients_thread
ON club_communication_recipients (thread_id, delivery_status);
CREATE TABLE IF NOT EXISTS club_communication_messages (
id bigserial PRIMARY KEY,
thread_id bigint NOT NULL,
sender_user_id bigint,
sender_member_id bigint,
body text NOT NULL,
body_format varchar(16) NOT NULL DEFAULT 'plain',
sent_at timestamptz NOT NULL DEFAULT now(),
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_communication_messages_thread
ON club_communication_messages (thread_id, sent_at);
CREATE TABLE IF NOT EXISTS club_events (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
event_type varchar(32) NOT NULL,
status varchar(32) NOT NULL DEFAULT 'planned',
title varchar(255) NOT NULL,
description text,
location varchar(255),
starts_at timestamptz NOT NULL,
ends_at timestamptz,
registration_deadline timestamptz,
organizer_user_id bigint,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
archived_at timestamptz
);
CREATE INDEX IF NOT EXISTS idx_club_events_club_starts
ON club_events (club_id, starts_at);
CREATE TABLE IF NOT EXISTS club_event_participants (
id bigserial PRIMARY KEY,
event_id bigint NOT NULL,
member_id bigint,
user_id bigint,
role_code varchar(32),
participation_status varchar(32) NOT NULL DEFAULT 'planned',
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_event_participants_event
ON club_event_participants (event_id, participation_status);
CREATE TABLE IF NOT EXISTS club_documents (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
document_type varchar(32) NOT NULL,
title varchar(255) NOT NULL,
description text,
status varchar(32) NOT NULL DEFAULT 'active',
visibility_scope varchar(32) NOT NULL DEFAULT 'board',
owner_user_id bigint,
current_version_no integer NOT NULL DEFAULT 1,
archived_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_documents_club_type
ON club_documents (club_id, document_type, status);
CREATE TABLE IF NOT EXISTS club_document_versions (
id bigserial PRIMARY KEY,
document_id bigint NOT NULL,
version_no integer NOT NULL,
file_name varchar(255) NOT NULL,
storage_path varchar(500) NOT NULL,
mime_type varchar(120),
file_size_bytes bigint,
checksum_sha256 varchar(64),
uploaded_by_user_id bigint,
uploaded_at timestamptz NOT NULL DEFAULT now(),
change_note text
);
CREATE UNIQUE INDEX IF NOT EXISTS uq_club_document_versions_document_version
ON club_document_versions (document_id, version_no);
CREATE TABLE IF NOT EXISTS club_document_links (
id bigserial PRIMARY KEY,
document_id bigint NOT NULL,
linked_entity_type varchar(32) NOT NULL,
linked_entity_id bigint NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_document_links_entity
ON club_document_links (linked_entity_type, linked_entity_id);
CREATE TABLE IF NOT EXISTS club_tasks (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
title varchar(255) NOT NULL,
task_type varchar(64),
description text,
status varchar(32) NOT NULL DEFAULT 'open',
priority varchar(16) NOT NULL DEFAULT 'normal',
due_at timestamptz,
remind_at timestamptz,
created_by_user_id bigint,
assigned_user_id bigint,
automation_source varchar(64),
automation_key varchar(255),
related_entity_type varchar(32),
related_entity_id bigint,
source_snapshot jsonb,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
completed_at timestamptz,
archived_at timestamptz
);
CREATE INDEX IF NOT EXISTS idx_club_tasks_club_status_due
ON club_tasks (club_id, status, due_at);
CREATE UNIQUE INDEX IF NOT EXISTS uq_club_tasks_automation_key
ON club_tasks (club_id, automation_key)
WHERE automation_key IS NOT NULL;
CREATE TABLE IF NOT EXISTS club_task_suppressions (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
automation_key varchar(255) NOT NULL,
suppression_token varchar(255) NOT NULL,
dismissed_by_user_id bigint,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX IF NOT EXISTS uq_club_task_suppressions_key
ON club_task_suppressions (club_id, automation_key);
CREATE INDEX IF NOT EXISTS idx_club_task_suppressions_lookup
ON club_task_suppressions (club_id, automation_key, suppression_token);
CREATE TABLE IF NOT EXISTS club_sponsors (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
name varchar(255) NOT NULL,
status varchar(32) NOT NULL DEFAULT 'lead',
website varchar(255),
email varchar(255),
phone varchar(80),
street varchar(255),
postal_code varchar(24),
city varchar(120),
notes text,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
archived_at timestamptz
);
CREATE UNIQUE INDEX IF NOT EXISTS uq_club_sponsors_name
ON club_sponsors (club_id, lower(name));
CREATE TABLE IF NOT EXISTS club_sponsor_contacts (
id bigserial PRIMARY KEY,
sponsor_id bigint NOT NULL,
first_name varchar(120),
last_name varchar(120),
role_title varchar(120),
email varchar(255),
phone varchar(80),
is_primary boolean NOT NULL DEFAULT false,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_sponsor_contacts_sponsor
ON club_sponsor_contacts (sponsor_id, is_primary DESC);
CREATE TABLE IF NOT EXISTS club_sponsor_contracts (
id bigserial PRIMARY KEY,
sponsor_id bigint NOT NULL,
title varchar(255) NOT NULL,
status varchar(32) NOT NULL DEFAULT 'draft',
starts_on date,
ends_on date,
annual_amount_cents bigint,
currency_code varchar(3) NOT NULL DEFAULT 'EUR',
notes text,
document_id bigint,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_sponsor_contracts_sponsor
ON club_sponsor_contracts (sponsor_id, status);
CREATE TABLE IF NOT EXISTS club_fee_rules (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
code varchar(64) NOT NULL,
name varchar(255) NOT NULL,
category varchar(32) NOT NULL DEFAULT 'membership',
billing_cycle varchar(16) NOT NULL DEFAULT 'monthly',
base_amount_cents bigint NOT NULL,
currency_code varchar(3) NOT NULL DEFAULT 'EUR',
age_from integer,
age_to integer,
is_family_rule boolean NOT NULL DEFAULT false,
is_reduction_rule boolean NOT NULL DEFAULT false,
valid_from date NOT NULL DEFAULT CURRENT_DATE,
valid_to date,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX IF NOT EXISTS uq_club_fee_rules_code
ON club_fee_rules (club_id, code);
CREATE TABLE IF NOT EXISTS club_fee_rule_assignments (
id bigserial PRIMARY KEY,
fee_rule_id bigint NOT NULL,
member_id bigint NOT NULL,
valid_from date NOT NULL DEFAULT CURRENT_DATE,
valid_to date,
discount_percent numeric(5,2),
fixed_amount_cents bigint,
notes text,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_fee_rule_assignments_member
ON club_fee_rule_assignments (member_id, valid_from DESC);
CREATE TABLE IF NOT EXISTS club_payment_accounts (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
account_name varchar(255) NOT NULL,
account_type varchar(32) NOT NULL DEFAULT 'bank',
iban varchar(34),
bic varchar(11),
bank_name varchar(255),
account_holder varchar(255),
is_default boolean NOT NULL DEFAULT false,
is_active boolean NOT NULL DEFAULT true,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_payment_accounts_club
ON club_payment_accounts (club_id, is_default DESC, is_active DESC);
CREATE TABLE IF NOT EXISTS club_sepa_mandates (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
member_id bigint,
debtor_name varchar(255) NOT NULL,
iban varchar(34) NOT NULL,
bic varchar(11),
mandate_reference varchar(80) NOT NULL,
signed_on date,
valid_from date,
revoked_at timestamptz,
status varchar(32) NOT NULL DEFAULT 'active',
history_note text,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX IF NOT EXISTS uq_club_sepa_mandates_reference
ON club_sepa_mandates (club_id, mandate_reference);
CREATE TABLE IF NOT EXISTS club_payment_claims (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
member_id bigint,
fee_rule_id bigint,
claim_type varchar(32) NOT NULL DEFAULT 'membership_fee',
status varchar(32) NOT NULL DEFAULT 'open',
due_on date NOT NULL,
amount_cents bigint NOT NULL,
currency_code varchar(3) NOT NULL DEFAULT 'EUR',
reminder_level integer NOT NULL DEFAULT 0,
last_reminder_at timestamptz,
notes text,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
settled_at timestamptz,
archived_at timestamptz
);
CREATE INDEX IF NOT EXISTS idx_club_payment_claims_club_status_due
ON club_payment_claims (club_id, status, due_on);
CREATE TABLE IF NOT EXISTS club_accounts (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
name varchar(160) NOT NULL,
account_holder varchar(255),
bank_name varchar(160),
iban varchar(34),
bic varchar(11),
account_type varchar(16) NOT NULL DEFAULT 'bank',
usage_type varchar(32) NOT NULL DEFAULT 'general',
currency_code varchar(3) NOT NULL DEFAULT 'EUR',
allow_sepa_collections boolean NOT NULL DEFAULT false,
allow_outgoing_payments boolean NOT NULL DEFAULT true,
is_default boolean NOT NULL DEFAULT false,
status varchar(16) NOT NULL DEFAULT 'active',
notes text,
sort_order integer NOT NULL DEFAULT 0,
archived_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_accounts_club_status
ON club_accounts (club_id, status, account_type);
CREATE INDEX IF NOT EXISTS idx_club_accounts_default
ON club_accounts (club_id, is_default);
CREATE TABLE IF NOT EXISTS club_payment_entries (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
payment_claim_id bigint,
payment_account_id bigint,
entry_type varchar(32) NOT NULL,
status varchar(32) NOT NULL DEFAULT 'booked',
booked_on date NOT NULL DEFAULT CURRENT_DATE,
amount_cents bigint NOT NULL,
currency_code varchar(3) NOT NULL DEFAULT 'EUR',
reference_text varchar(255),
external_reference varchar(255),
created_by_user_id bigint,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_payment_entries_club_booked
ON club_payment_entries (club_id, booked_on DESC);
CREATE TABLE IF NOT EXISTS club_invoice_parties (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
party_type varchar(32) NOT NULL,
name varchar(255) NOT NULL,
contact_name varchar(255),
email varchar(255),
phone varchar(80),
street varchar(255),
postal_code varchar(24),
city varchar(120),
country_code varchar(2) DEFAULT 'DE',
iban varchar(34),
bic varchar(11),
tax_identifier varchar(64),
notes text,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_invoice_parties_club_type
ON club_invoice_parties (club_id, party_type);
CREATE TABLE IF NOT EXISTS club_invoices (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
invoice_direction varchar(16) NOT NULL,
invoice_type varchar(32) NOT NULL,
status varchar(32) NOT NULL DEFAULT 'draft',
invoice_number varchar(64),
external_reference varchar(255),
party_id bigint,
issued_on date,
due_on date,
paid_on date,
net_amount_cents bigint NOT NULL DEFAULT 0,
tax_amount_cents bigint NOT NULL DEFAULT 0,
gross_amount_cents bigint NOT NULL DEFAULT 0,
currency_code varchar(3) NOT NULL DEFAULT 'EUR',
description text,
document_id bigint,
created_by_user_id bigint,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
archived_at timestamptz
);
CREATE INDEX IF NOT EXISTS idx_club_invoices_club_direction_status
ON club_invoices (club_id, invoice_direction, status, due_on);
CREATE TABLE IF NOT EXISTS club_invoice_items (
id bigserial PRIMARY KEY,
invoice_id bigint NOT NULL,
line_no integer NOT NULL DEFAULT 1,
description text NOT NULL,
quantity numeric(12,2) NOT NULL DEFAULT 1,
unit_price_cents bigint NOT NULL DEFAULT 0,
tax_rate numeric(5,2) NOT NULL DEFAULT 0,
total_cents bigint NOT NULL DEFAULT 0,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX IF NOT EXISTS uq_club_invoice_items_line
ON club_invoice_items (invoice_id, line_no);
CREATE TABLE IF NOT EXISTS club_history_entries (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
entity_type varchar(32) NOT NULL,
entity_id bigint NOT NULL,
action_type varchar(32) NOT NULL,
actor_user_id bigint,
actor_member_id bigint,
old_value_json jsonb,
new_value_json jsonb,
summary text,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_history_entries_club_entity
ON club_history_entries (club_id, entity_type, entity_id, created_at DESC);
CREATE TABLE IF NOT EXISTS club_user_roles (
id bigserial PRIMARY KEY,
club_id bigint NOT NULL,
user_id bigint,
member_id bigint,
role_code varchar(32) NOT NULL,
valid_from date NOT NULL DEFAULT CURRENT_DATE,
valid_to date,
is_primary boolean NOT NULL DEFAULT false,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_club_user_roles_club_user
ON club_user_roles (club_id, user_id, role_code);
ALTER TABLE club_tasks
ADD COLUMN IF NOT EXISTS task_type varchar(64),
ADD COLUMN IF NOT EXISTS automation_source varchar(64),
ADD COLUMN IF NOT EXISTS automation_key varchar(255),
ADD COLUMN IF NOT EXISTS source_snapshot jsonb;
ALTER TABLE club_requests
ADD COLUMN IF NOT EXISTS workflow_stage varchar(64);
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_requests_type') THEN
ALTER TABLE club_requests
ADD CONSTRAINT chk_club_requests_type
CHECK (request_type IN ('contact', 'trial_training', 'membership', 'sponsoring'));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_requests_status') THEN
ALTER TABLE club_requests
ADD CONSTRAINT chk_club_requests_status
CHECK (status IN ('open', 'in_progress', 'waiting', 'converted', 'rejected', 'archived'));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_events_type') THEN
ALTER TABLE club_events
ADD CONSTRAINT chk_club_events_type
CHECK (event_type IN ('training', 'match', 'club_event', 'meeting', 'deadline'));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_tasks_status') THEN
ALTER TABLE club_tasks
ADD CONSTRAINT chk_club_tasks_status
CHECK (status IN ('open', 'in_progress', 'waiting', 'done', 'cancelled', 'archived'));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_fee_rules_cycle') THEN
ALTER TABLE club_fee_rules
ADD CONSTRAINT chk_club_fee_rules_cycle
CHECK (billing_cycle IN ('monthly', 'quarterly', 'half_yearly', 'yearly', 'one_time'));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_payment_claims_status') THEN
ALTER TABLE club_payment_claims
ADD CONSTRAINT chk_club_payment_claims_status
CHECK (status IN ('open', 'partially_paid', 'paid', 'written_off', 'cancelled'));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_accounts_type') THEN
ALTER TABLE club_accounts
ADD CONSTRAINT chk_club_accounts_type
CHECK (account_type IN ('bank', 'cash', 'virtual'));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_accounts_usage') THEN
ALTER TABLE club_accounts
ADD CONSTRAINT chk_club_accounts_usage
CHECK (usage_type IN ('general', 'membership_fees', 'donations', 'expenses', 'reserve', 'petty_cash'));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_accounts_status') THEN
ALTER TABLE club_accounts
ADD CONSTRAINT chk_club_accounts_status
CHECK (status IN ('active', 'inactive', 'archived'));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_invoices_direction') THEN
ALTER TABLE club_invoices
ADD CONSTRAINT chk_club_invoices_direction
CHECK (invoice_direction IN ('incoming', 'outgoing'));
END IF;
END
$$;
COMMIT;