-- Drop kanban template scaffold. DROP TABLE IF EXISTS kanban_card; -- Seminarhof Drawehn core schema. -- Multi-tenant from day one (venue_id on every domain row). -- All money stored as integer cents (euro_cents) to avoid float drift. -- ───────────────────────────────────────────────────────────────────── -- Tenancy CREATE TABLE IF NOT EXISTS venue ( venue_id TEXT PRIMARY KEY, name TEXT NOT NULL, slug TEXT NOT NULL UNIQUE, locale_default TEXT NOT NULL DEFAULT 'de', meal_window_breakfast TEXT NOT NULL DEFAULT '07:00-10:00', meal_window_lunch TEXT NOT NULL DEFAULT '12:00-14:00', meal_window_dinner TEXT NOT NULL DEFAULT '18:00-20:00', created_at TEXT NOT NULL DEFAULT (datetime('now')) ); -- ───────────────────────────────────────────────────────────────────── -- Identity CREATE TABLE IF NOT EXISTS app_user ( user_id TEXT PRIMARY KEY, email TEXT NOT NULL UNIQUE, name TEXT NOT NULL, locale TEXT NOT NULL DEFAULT 'de', role TEXT NOT NULL, -- 'admin' | 'client' | 'owner' created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE IF NOT EXISTS client ( client_id TEXT PRIMARY KEY, venue_id TEXT NOT NULL REFERENCES venue(venue_id), name TEXT, is_stammgruppe INTEGER NOT NULL DEFAULT 0, billing_address TEXT, contact_email TEXT, contact_phone TEXT, website TEXT, notes TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE IF NOT EXISTS client_member ( client_id TEXT NOT NULL REFERENCES client(client_id), user_id TEXT NOT NULL REFERENCES app_user(user_id), role TEXT NOT NULL DEFAULT 'organiser', -- 'organiser' | 'colleague' PRIMARY KEY (client_id, user_id) ); -- ───────────────────────────────────────────────────────────────────── -- Physical inventory CREATE TABLE IF NOT EXISTS bathroom ( bathroom_id TEXT PRIMARY KEY, venue_id TEXT NOT NULL REFERENCES venue(venue_id), label TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS room ( room_id TEXT PRIMARY KEY, venue_id TEXT NOT NULL REFERENCES venue(venue_id), number INTEGER NOT NULL, beds INTEGER NOT NULL, room_type TEXT NOT NULL, -- 'MBZ' | 'DZ' | 'EZ_only' building TEXT NOT NULL, -- 'gaestehaus' | 'haupthaus' floor TEXT NOT NULL, -- 'eg' | 'og' bathroom_id TEXT REFERENCES bathroom(bathroom_id), wheelchair INTEGER NOT NULL DEFAULT 0, ground_floor INTEGER NOT NULL DEFAULT 0, quiet INTEGER NOT NULL DEFAULT 0, shared_bath INTEGER NOT NULL DEFAULT 0, dogs_allowed INTEGER NOT NULL DEFAULT 0, double_bed_140 INTEGER NOT NULL DEFAULT 0, allergy_friendly INTEGER NOT NULL DEFAULT 0, surcharge_eur_cents INTEGER NOT NULL DEFAULT 0, UNIQUE (venue_id, number) ); CREATE TABLE IF NOT EXISTS seminar_room ( seminar_room_id TEXT PRIMARY KEY, venue_id TEXT NOT NULL REFERENCES venue(venue_id), name TEXT NOT NULL ); -- ───────────────────────────────────────────────────────────────────── -- Bookings CREATE TABLE IF NOT EXISTS booking ( booking_id TEXT PRIMARY KEY, venue_id TEXT NOT NULL REFERENCES venue(venue_id), client_id TEXT NOT NULL REFERENCES client(client_id), event_name TEXT NOT NULL, start_date TEXT, end_date TEXT, status TEXT NOT NULL, -- enquiry | form_received | reserved | deposit_paid | confirmed | ended | cancelled half_house INTEGER NOT NULL DEFAULT 0, online_ad INTEGER NOT NULL DEFAULT 0, cover_image_url TEXT, short_description TEXT, organiser_website TEXT, expected_persons INTEGER, meal_time_breakfast TEXT, meal_time_lunch TEXT, meal_time_dinner TEXT, daily_plan TEXT, arrival_time TEXT, departure_time TEXT, payment_method TEXT NOT NULL DEFAULT 'transfer', -- 'cash' | 'transfer' agb_year INTEGER, agb_accepted_at TEXT, privacy_accepted_at TEXT, notes TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_booking_venue_dates ON booking (venue_id, start_date, end_date); CREATE INDEX IF NOT EXISTS idx_booking_client ON booking (client_id); CREATE INDEX IF NOT EXISTS idx_booking_status ON booking (venue_id, status); CREATE TABLE IF NOT EXISTS booking_extra ( booking_extra_id TEXT PRIMARY KEY, booking_id TEXT NOT NULL REFERENCES booking(booking_id), kind TEXT NOT NULL, -- 'cake' | 'second_seminar_room' | 'early_arrival' | 'bedlinen' | 'massage_bench' | 'sauna' qty INTEGER NOT NULL DEFAULT 1, on_dates TEXT, -- JSON array of dates for cake-day picker unit_price_cents INTEGER NOT NULL DEFAULT 0, note TEXT ); -- ───────────────────────────────────────────────────────────────────── -- Participants CREATE TABLE IF NOT EXISTS participant ( participant_id TEXT PRIMARY KEY, booking_id TEXT NOT NULL REFERENCES booking(booking_id), name TEXT NOT NULL, email TEXT, age INTEGER, kind TEXT NOT NULL DEFAULT 'overnight', -- 'overnight' | 'day_guest' dietary_tag TEXT, -- 'omnivore' | 'vegetarian' | 'vegan' | 'gluten_free' | 'lactose_free' | 'pescatarian' | 'unknown' free_text TEXT, self_form_token TEXT UNIQUE, self_form_submitted_at TEXT, notes TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_participant_booking ON participant (booking_id); CREATE TABLE IF NOT EXISTS participant_allergy ( participant_id TEXT NOT NULL REFERENCES participant(participant_id), allergen TEXT NOT NULL, -- 'nuts' | 'soy' | 'gluten_strict' | 'dairy' | 'egg' | 'fish' | 'other' severity TEXT NOT NULL DEFAULT 'standard', -- 'standard' | 'separate_prep' note TEXT, PRIMARY KEY (participant_id, allergen) ); CREATE TABLE IF NOT EXISTS room_assignment ( booking_id TEXT NOT NULL REFERENCES booking(booking_id), participant_id TEXT NOT NULL REFERENCES participant(participant_id), room_id TEXT NOT NULL REFERENCES room(room_id), PRIMARY KEY (booking_id, participant_id) ); CREATE INDEX IF NOT EXISTS idx_room_assignment_room ON room_assignment (room_id); -- ───────────────────────────────────────────────────────────────────── -- Invoicing CREATE TABLE IF NOT EXISTS invoice ( invoice_id TEXT PRIMARY KEY, booking_id TEXT NOT NULL REFERENCES booking(booking_id), kind TEXT NOT NULL, -- 'deposit' | 'final' invoice_number TEXT NOT NULL UNIQUE, issued_on TEXT NOT NULL, due_on TEXT, paid_on TEXT, amount_cents INTEGER NOT NULL, status TEXT NOT NULL DEFAULT 'outstanding', -- 'outstanding' | 'paid' | 'cancelled' pdf_url TEXT, datev_ref TEXT ); CREATE INDEX IF NOT EXISTS idx_invoice_booking ON invoice (booking_id); -- ───────────────────────────────────────────────────────────────────── -- Audit CREATE TABLE IF NOT EXISTS audit_log ( audit_id TEXT PRIMARY KEY, venue_id TEXT NOT NULL REFERENCES venue(venue_id), user_id TEXT REFERENCES app_user(user_id), entity TEXT NOT NULL, entity_id TEXT NOT NULL, field TEXT, before_value TEXT, after_value TEXT, action TEXT NOT NULL, -- 'create' | 'update' | 'delete' at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_audit_entity ON audit_log (entity, entity_id);