create type system_role as enum ('USER', 'SYS_ADMIN'); create type group_role as enum ('MEMBER', 'GROUP_ADMIN'); create table users( id bigserial primary key, email text not null unique, display_name text, password_hash text not null, system_role system_role not null default 'USER', created_at timestamptz not null default now() ); create table sessions( id bigserial primary key, user_id bigint not null references users(id) on delete cascade, token_hash text not null unique, expires_at timestamptz not null, created_at timestamptz not null default now() ); create table groups( id bigserial primary key, name text not null, invite_code text not null unique, created_by bigint not null references users(id), created_at timestamptz not null default now() ); create table group_members( group_id bigint not null references groups(id) on delete cascade, user_id bigint not null references users(id) on delete cascade, role group_role not null default 'MEMBER', created_at timestamptz not null default now(), primary key(group_id, user_id) ); create type spending_necessity as enum ('NECESSARY', 'BOTH', 'UNNECESSARY'); create table receipts( id bigserial primary key, content_type text not null, bytes bytea not null, created_by bigint not null references users(id), created_at timestamptz not null default now() ); create table spendings( id bigserial primary key, group_id bigint not null references groups(id) on delete cascade, created_by bigint not null references users(id), amount_dollars numeric(12,2) not null check(amount_dollars >= 0), occurred_at date not null, necessity spending_necessity not null, purchase_type text not null, notes text, receipt_id bigint references receipts(id) on delete set null, created_at timestamptz not null default now() ); create index idx_spendings_group_occurred on spendings(group_id, occurred_at desc); create index idx_spendings_receipt on spendings(receipt_id); create table user_settings( user_id bigint primary key references users(id) on delete cascade, data jsonb not null default '{}'::jsonb, updated_at timestamptz not null default now() ); create table reports( id bigserial primary key, created_by bigint not null references users(id), title text not null, body text not null, status text not null default 'OPEN', created_at timestamptz not null default now() );