-- ============================================================ -- Perauset schema — converted from Postgres to SQLite -- UUIDs → TEXT, BOOLEAN → INTEGER (0/1), JSON columns → TEXT, -- TIMESTAMPTZ/DATE → TEXT, enums → TEXT CHECK constraints, -- arrays → TEXT (JSON), no schemas, no triggers, no PL/pgSQL -- ============================================================ -- ============================================================ -- Users (replaced by better-auth 0002 — defined here as -- a placeholder so FKs can be deferred until 0002 runs) -- ============================================================ CREATE TABLE IF NOT EXISTS user ( id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), email TEXT NOT NULL UNIQUE, name TEXT, email_verified INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); -- ============================================================ -- User profile -- ============================================================ CREATE TABLE user_profile ( profile_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), user_id TEXT NOT NULL UNIQUE REFERENCES user(id) ON DELETE CASCADE, date_of_birth TEXT, nationality TEXT, notes TEXT, emergency_contact_name TEXT, emergency_contact_phone TEXT, mobile_number TEXT, whatsapp_number TEXT, avatar_url TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); -- ============================================================ -- Audit log (no triggers in SQLite — app writes rows directly) -- ============================================================ CREATE TABLE audit_log ( audit_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), table_name TEXT NOT NULL, record_id TEXT NOT NULL, action TEXT NOT NULL, user_id TEXT REFERENCES user(id), changed_fields TEXT, occurred_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_audit_log_table_record ON audit_log(table_name, record_id); CREATE INDEX idx_audit_log_user ON audit_log(user_id); CREATE INDEX idx_audit_log_occurred ON audit_log(occurred_at); -- ============================================================ -- Stay requests -- ============================================================ CREATE TABLE stay_request ( request_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), user_id TEXT NOT NULL REFERENCES user(id), request_type TEXT NOT NULL DEFAULT 'guest' CHECK (request_type IN ('guest','volunteer','staff','facilitator','day_visit')), requested_start_at TEXT NOT NULL, requested_end_at TEXT NOT NULL, retreat_id TEXT, notes TEXT, status TEXT NOT NULL DEFAULT 'submitted' CHECK (status IN ('submitted','under_review','approved','rejected','cancelled')), reviewed_by_user_id TEXT REFERENCES user(id), reviewed_at TEXT, review_notes TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_stay_request_user ON stay_request(user_id); CREATE INDEX idx_stay_request_status ON stay_request(status); -- ============================================================ -- Stays -- ============================================================ CREATE TABLE stay ( stay_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), user_id TEXT NOT NULL REFERENCES user(id), stay_type TEXT NOT NULL DEFAULT 'guest' CHECK (stay_type IN ('guest','volunteer','staff','facilitator','day_visitor')), source_request_id TEXT REFERENCES stay_request(request_id), retreat_id TEXT, start_at TEXT NOT NULL, end_at TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','confirmed','checked_in','checked_out','cancelled')), checked_in_at TEXT, checked_out_at TEXT, created_by_user_id TEXT NOT NULL REFERENCES user(id), updated_by_user_id TEXT REFERENCES user(id), created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_stay_user ON stay(user_id); CREATE INDEX idx_stay_status ON stay(status); CREATE INDEX idx_stay_dates ON stay(start_at, end_at); -- ============================================================ -- Rooms -- ============================================================ CREATE TABLE room ( room_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), code TEXT NOT NULL UNIQUE, name TEXT NOT NULL, room_type TEXT NOT NULL DEFAULT 'private' CHECK (room_type IN ('private','shared','dorm','facilitator','staff')), capacity INTEGER NOT NULL DEFAULT 1, is_active INTEGER NOT NULL DEFAULT 1, notes TEXT, price_per_night REAL, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE room_block ( block_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), room_id TEXT NOT NULL REFERENCES room(room_id), block_type TEXT NOT NULL DEFAULT 'maintenance' CHECK (block_type IN ('maintenance','retreat_reserved','admin_hold','other')), retreat_id TEXT, starts_at TEXT NOT NULL, ends_at TEXT NOT NULL, reason TEXT, created_by_user_id TEXT NOT NULL REFERENCES user(id), created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_room_block_room ON room_block(room_id); CREATE INDEX idx_room_block_dates ON room_block(starts_at, ends_at); CREATE TABLE room_request ( request_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), stay_id TEXT NOT NULL REFERENCES stay(stay_id), requested_room_type TEXT CHECK (requested_room_type IN ('private','shared','dorm','facilitator','staff')), preference_notes TEXT, status TEXT NOT NULL DEFAULT 'submitted' CHECK (status IN ('submitted','reviewed','satisfied','unsatisfied','cancelled')), reviewed_by_user_id TEXT REFERENCES user(id), reviewed_at TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE room_allocation ( allocation_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), stay_id TEXT NOT NULL REFERENCES stay(stay_id), room_id TEXT NOT NULL REFERENCES room(room_id), starts_at TEXT NOT NULL, ends_at TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'planned' CHECK (status IN ('planned','active','completed','cancelled')), allocation_reason TEXT, created_by_user_id TEXT NOT NULL REFERENCES user(id), updated_by_user_id TEXT REFERENCES user(id), created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_room_allocation_room ON room_allocation(room_id); CREATE INDEX idx_room_allocation_stay ON room_allocation(stay_id); CREATE INDEX idx_room_allocation_dates ON room_allocation(starts_at, ends_at); -- ============================================================ -- Boats -- ============================================================ CREATE TABLE boat_route ( route_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), name TEXT NOT NULL, origin TEXT NOT NULL, destination TEXT NOT NULL, is_active INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE boat ( boat_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), name TEXT NOT NULL, passenger_capacity INTEGER NOT NULL, cargo_capacity_kg REAL, is_active INTEGER NOT NULL DEFAULT 1, notes TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE boat_trip ( trip_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), boat_id TEXT REFERENCES boat(boat_id), route_id TEXT NOT NULL REFERENCES boat_route(route_id), scheduled_departure_at TEXT NOT NULL, scheduled_arrival_at TEXT, status TEXT NOT NULL DEFAULT 'planned' CHECK (status IN ('planned','open','full','closed','cancelled','completed')), notes TEXT, created_by_user_id TEXT NOT NULL REFERENCES user(id), updated_by_user_id TEXT REFERENCES user(id), created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_boat_trip_status ON boat_trip(status); CREATE INDEX idx_boat_trip_departure ON boat_trip(scheduled_departure_at); CREATE TABLE boat_reservation_request ( request_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), trip_id TEXT NOT NULL REFERENCES boat_trip(trip_id), user_id TEXT NOT NULL REFERENCES user(id), stay_id TEXT REFERENCES stay(stay_id), retreat_id TEXT, requested_seats INTEGER NOT NULL DEFAULT 1, cargo_notes TEXT, special_requirements TEXT, priority_reason TEXT, status TEXT NOT NULL DEFAULT 'submitted' CHECK (status IN ('submitted','under_review','confirmed','waitlisted','declined','cancelled')), reviewed_by_user_id TEXT REFERENCES user(id), reviewed_at TEXT, review_notes TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_boat_reservation_trip ON boat_reservation_request(trip_id); CREATE INDEX idx_boat_reservation_user ON boat_reservation_request(user_id); CREATE INDEX idx_boat_reservation_status ON boat_reservation_request(status); CREATE TABLE boat_manifest_entry ( entry_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), trip_id TEXT NOT NULL REFERENCES boat_trip(trip_id), reservation_request_id TEXT REFERENCES boat_reservation_request(request_id), user_id TEXT NOT NULL REFERENCES user(id), stay_id TEXT REFERENCES stay(stay_id), status TEXT NOT NULL DEFAULT 'confirmed' CHECK (status IN ('confirmed','boarded','no_show','cancelled')), confirmed_by_user_id TEXT NOT NULL REFERENCES user(id), created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_boat_manifest_trip ON boat_manifest_entry(trip_id); -- ============================================================ -- Tasks -- ============================================================ CREATE TABLE task_template ( template_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), title TEXT NOT NULL, description TEXT, category TEXT NOT NULL DEFAULT 'operations' CHECK (category IN ('operations','maintenance','kitchen','retreat','transport','housekeeping')), is_claimable INTEGER NOT NULL DEFAULT 0, default_location TEXT, estimated_minutes INTEGER, recurrence_cron TEXT, is_active INTEGER NOT NULL DEFAULT 1, created_by_user_id TEXT NOT NULL REFERENCES user(id), created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE task_instance ( task_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), template_id TEXT REFERENCES task_template(template_id), title TEXT NOT NULL, description TEXT, category TEXT NOT NULL DEFAULT 'operations' CHECK (category IN ('operations','maintenance','kitchen','retreat','transport','housekeeping')), location TEXT, assigned_role TEXT, linked_retreat_id TEXT, linked_stay_id TEXT REFERENCES stay(stay_id), scheduled_start_at TEXT, scheduled_end_at TEXT, status TEXT NOT NULL DEFAULT 'planned' CHECK (status IN ('planned','open','assigned','in_progress','blocked','completed','cancelled')), created_by_user_id TEXT NOT NULL REFERENCES user(id), created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_task_instance_status ON task_instance(status); CREATE INDEX idx_task_instance_category ON task_instance(category); CREATE INDEX idx_task_instance_template ON task_instance(template_id); CREATE TABLE task_assignment ( assignment_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), task_instance_id TEXT NOT NULL REFERENCES task_instance(task_id), assigned_user_id TEXT NOT NULL REFERENCES user(id), assigned_by_user_id TEXT NOT NULL REFERENCES user(id), status TEXT NOT NULL DEFAULT 'proposed' CHECK (status IN ('proposed','accepted','declined','reassigned','completed')), notes TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_task_assignment_task ON task_assignment(task_instance_id); CREATE INDEX idx_task_assignment_user ON task_assignment(assigned_user_id); -- ============================================================ -- Notifications -- ============================================================ CREATE TABLE notification ( notification_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), user_id TEXT NOT NULL REFERENCES user(id), type TEXT NOT NULL, title TEXT NOT NULL, body TEXT, entity_type TEXT, entity_id TEXT, read_at TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_notification_user ON notification(user_id, read_at, created_at); -- ============================================================ -- Retreats -- ============================================================ CREATE TABLE retreat ( retreat_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), slug TEXT NOT NULL UNIQUE, name TEXT NOT NULL, description TEXT, start_at TEXT NOT NULL, end_at TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN ('draft','published','active','completed','cancelled')), capacity INTEGER, default_check_in_at TEXT, default_check_out_at TEXT, created_by_user_id TEXT NOT NULL REFERENCES user(id), created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_retreat_status ON retreat(status); CREATE INDEX idx_retreat_dates ON retreat(start_at, end_at); CREATE TABLE retreat_person ( retreat_person_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), retreat_id TEXT NOT NULL REFERENCES retreat(retreat_id) ON DELETE CASCADE, user_id TEXT REFERENCES user(id), stay_id TEXT REFERENCES stay(stay_id), full_name TEXT, email TEXT, phone TEXT, role_type TEXT NOT NULL DEFAULT 'participant' CHECK (role_type IN ('participant','facilitator','assistant_facilitator','organizer','support_staff')), attendance_status TEXT NOT NULL DEFAULT 'registered' CHECK (attendance_status IN ('invited','registered','confirmed','checked_in','cancelled')), arrival_override_at TEXT, departure_override_at TEXT, notes TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE(retreat_id, user_id) ); CREATE INDEX idx_retreat_person_retreat ON retreat_person(retreat_id); CREATE INDEX idx_retreat_person_user ON retreat_person(user_id); CREATE TABLE retreat_schedule_item ( item_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), retreat_id TEXT NOT NULL REFERENCES retreat(retreat_id) ON DELETE CASCADE, title TEXT NOT NULL, description TEXT, starts_at TEXT NOT NULL, ends_at TEXT NOT NULL, location TEXT, lead_retreat_person_id TEXT REFERENCES retreat_person(retreat_person_id), created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_retreat_schedule_retreat ON retreat_schedule_item(retreat_id); -- ============================================================ -- Kitchen / Dietary -- ============================================================ CREATE TABLE dietary_profile ( profile_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), user_id TEXT NOT NULL UNIQUE REFERENCES user(id) ON DELETE CASCADE, is_vegan INTEGER NOT NULL DEFAULT 0, is_vegetarian INTEGER NOT NULL DEFAULT 0, is_gluten_free INTEGER NOT NULL DEFAULT 0, is_dairy_free INTEGER NOT NULL DEFAULT 0, has_nut_allergy INTEGER NOT NULL DEFAULT 0, allergy_notes TEXT, other_requirements TEXT, dislikes TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE meal_service ( service_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), service_type TEXT NOT NULL CHECK (service_type IN ('breakfast','lunch','dinner','snack')), service_date TEXT NOT NULL, retreat_id TEXT REFERENCES retreat(retreat_id), planned_headcount INTEGER, menu_notes TEXT, status TEXT NOT NULL DEFAULT 'planned' CHECK (status IN ('planned','ready','served','completed')), created_by_user_id TEXT NOT NULL REFERENCES user(id), updated_by_user_id TEXT REFERENCES user(id), created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_meal_service_date ON meal_service(service_date); CREATE INDEX idx_meal_service_type ON meal_service(service_type); CREATE INDEX idx_meal_service_retreat ON meal_service(retreat_id); CREATE TABLE meal_attendance ( attendance_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), meal_service_id TEXT NOT NULL REFERENCES meal_service(service_id) ON DELETE CASCADE, user_id TEXT NOT NULL REFERENCES user(id), stay_id TEXT REFERENCES stay(stay_id), source_type TEXT NOT NULL DEFAULT 'manual' CHECK (source_type IN ('stay','retreat','manual')), status TEXT NOT NULL DEFAULT 'expected' CHECK (status IN ('expected','confirmed','cancelled','served')), notes TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE(meal_service_id, user_id) ); CREATE INDEX idx_meal_attendance_service ON meal_attendance(meal_service_id); CREATE INDEX idx_meal_attendance_user ON meal_attendance(user_id); -- ============================================================ -- Inventory -- ============================================================ CREATE TABLE inventory_item ( item_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), name TEXT NOT NULL, unit TEXT NOT NULL DEFAULT 'unit', current_quantity REAL NOT NULL DEFAULT 0, minimum_quantity REAL NOT NULL DEFAULT 0, is_active INTEGER NOT NULL DEFAULT 1, category TEXT NOT NULL DEFAULT 'operations', subcategory TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_inventory_item_active ON inventory_item(is_active); CREATE INDEX idx_inventory_item_category ON inventory_item(category); CREATE TABLE inventory_request ( request_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), requested_by_user_id TEXT NOT NULL REFERENCES user(id), item_id TEXT REFERENCES inventory_item(item_id), item_name TEXT NOT NULL, quantity REAL NOT NULL, unit TEXT NOT NULL, purpose TEXT, status TEXT NOT NULL DEFAULT 'submitted' CHECK (status IN ('submitted','approved','rejected','fulfilled','cancelled')), reviewed_by_user_id TEXT REFERENCES user(id), reviewed_at TEXT, review_notes TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_inventory_request_status ON inventory_request(status); CREATE INDEX idx_inventory_request_user ON inventory_request(requested_by_user_id); CREATE TABLE inventory_transaction ( transaction_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), item_id TEXT NOT NULL REFERENCES inventory_item(item_id), quantity_change REAL NOT NULL, reason TEXT NOT NULL, notes TEXT, created_by_user_id TEXT NOT NULL REFERENCES user(id), created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_inventory_transaction_item ON inventory_transaction(item_id); CREATE INDEX idx_inventory_transaction_created ON inventory_transaction(created_at); -- ============================================================ -- Finance -- ============================================================ CREATE TABLE finance_record ( finance_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), name TEXT NOT NULL, description TEXT, amount REAL NOT NULL, currency TEXT NOT NULL DEFAULT 'EUR', record_type TEXT NOT NULL DEFAULT 'expense', amount_egp REAL, purchased_by_user_id TEXT REFERENCES user(id), purchased_at TEXT NOT NULL DEFAULT (datetime('now')), category TEXT NOT NULL DEFAULT 'operations', created_by_user_id TEXT NOT NULL REFERENCES user(id), created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_finance_record_category ON finance_record(category); CREATE INDEX idx_finance_record_purchased_at ON finance_record(purchased_at); CREATE INDEX idx_finance_record_purchased_by ON finance_record(purchased_by_user_id); CREATE TABLE finance_link ( link_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), finance_id TEXT NOT NULL REFERENCES finance_record(finance_id) ON DELETE CASCADE, entity_type TEXT NOT NULL, entity_id TEXT NOT NULL, notes TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_finance_link_finance ON finance_link(finance_id); CREATE INDEX idx_finance_link_entity ON finance_link(entity_type, entity_id); -- ============================================================ -- Exchange rates -- ============================================================ CREATE TABLE exchange_rate ( rate_id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('AB89',1+(abs(random())%4),1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6)))), base_currency TEXT NOT NULL DEFAULT 'EGP', target_currency TEXT NOT NULL, rate REAL NOT NULL, fetched_at TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE UNIQUE INDEX idx_exchange_rate_unique ON exchange_rate(base_currency, target_currency, fetched_at); CREATE INDEX idx_exchange_rate_date ON exchange_rate(fetched_at);