{
  "registry": [
    [
      "index",
      "membership_resource",
      "memberships",
      "CREATE UNIQUE INDEX membership_resource ON memberships(business_id,resource_id) WHERE resource_id IS NOT NULL"
    ],
    [
      "table",
      "access_tokens",
      "access_tokens",
      "CREATE TABLE access_tokens(\n id TEXT PRIMARY KEY,token_hash TEXT UNIQUE NOT NULL,kind TEXT NOT NULL CHECK(kind IN ('invite','reset')),\n business_id TEXT REFERENCES businesses(id),user_id TEXT REFERENCES users(id),email TEXT NOT NULL,\n role TEXT CHECK(role IN ('owner','staff')),resource_id TEXT,expires REAL NOT NULL,used INTEGER NOT NULL DEFAULT 0 CHECK(used IN (0,1)),\n created_at TEXT NOT NULL,created_by TEXT REFERENCES users(id))"
    ],
    [
      "table",
      "businesses",
      "businesses",
      "CREATE TABLE businesses(id TEXT PRIMARY KEY, slug TEXT UNIQUE NOT NULL, name TEXT NOT NULL, enabled INTEGER NOT NULL DEFAULT 1)"
    ],
    [
      "table",
      "dataset_identity",
      "dataset_identity",
      "CREATE TABLE dataset_identity(id INTEGER PRIMARY KEY CHECK(id=1), dataset_id TEXT NOT NULL)"
    ],
    [
      "table",
      "domains",
      "domains",
      "CREATE TABLE domains(hostname TEXT PRIMARY KEY, business_id TEXT NOT NULL REFERENCES businesses(id))"
    ],
    [
      "table",
      "identity_audit",
      "identity_audit",
      "CREATE TABLE identity_audit(id INTEGER PRIMARY KEY AUTOINCREMENT,business_id TEXT REFERENCES businesses(id),actor_id TEXT,target_id TEXT NOT NULL,action TEXT NOT NULL,detail TEXT NOT NULL,created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "limits",
      "limits",
      "CREATE TABLE limits(key TEXT PRIMARY KEY, count INTEGER NOT NULL, expires REAL NOT NULL)"
    ],
    [
      "table",
      "memberships",
      "memberships",
      "CREATE TABLE memberships(\n id TEXT PRIMARY KEY,business_id TEXT NOT NULL REFERENCES businesses(id),user_id TEXT NOT NULL REFERENCES users(id),\n role TEXT NOT NULL CHECK(role IN ('owner','staff')),enabled INTEGER NOT NULL DEFAULT 1 CHECK(enabled IN (0,1)),\n version INTEGER NOT NULL DEFAULT 1,resource_id TEXT,created_at TEXT NOT NULL,UNIQUE(business_id,user_id))"
    ],
    [
      "table",
      "registry_meta",
      "registry_meta",
      "CREATE TABLE registry_meta(key TEXT PRIMARY KEY,value TEXT NOT NULL)"
    ],
    [
      "table",
      "registry_operations",
      "registry_operations",
      "CREATE TABLE registry_operations(key TEXT NOT NULL,user_id TEXT NOT NULL REFERENCES users(id),fingerprint TEXT NOT NULL,result TEXT NOT NULL,PRIMARY KEY(key,user_id))"
    ],
    [
      "table",
      "sessions",
      "sessions",
      "CREATE TABLE sessions(token TEXT PRIMARY KEY, business_id TEXT NOT NULL REFERENCES businesses(id), subject TEXT NOT NULL, kind TEXT NOT NULL, csrf TEXT NOT NULL, expires REAL NOT NULL, portal_version INTEGER NOT NULL DEFAULT 0)"
    ],
    [
      "table",
      "users",
      "users",
      "CREATE TABLE users(id TEXT PRIMARY KEY, business_id TEXT NOT NULL REFERENCES businesses(id), email TEXT UNIQUE NOT NULL, password TEXT NOT NULL, display_name TEXT NOT NULL DEFAULT '', disabled INTEGER NOT NULL DEFAULT 0 CHECK(disabled IN (0,1)))"
    ]
  ],
  "tenant": [
    [
      "index",
      "calendar_interval",
      "calendar_bookings",
      "CREATE INDEX calendar_interval ON calendar_bookings(start_utc,end_utc)"
    ],
    [
      "index",
      "calendar_job",
      "calendar_bookings",
      "CREATE INDEX calendar_job ON calendar_bookings(job_id)"
    ],
    [
      "index",
      "catalogue_active_name",
      "materials_catalogue",
      "CREATE UNIQUE INDEX catalogue_active_name ON materials_catalogue(name_key) WHERE archived=0"
    ],
    [
      "index",
      "invoice_active_quote",
      "invoices",
      "CREATE UNIQUE INDEX invoice_active_quote ON invoices(quote_id) WHERE status IN ('draft','issued')"
    ],
    [
      "index",
      "job_photos_job",
      "job_photos",
      "CREATE INDEX job_photos_job ON job_photos(job_id,archived,position)"
    ],
    [
      "index",
      "jobs_customer",
      "jobs",
      "CREATE INDEX jobs_customer ON jobs(customer_id)"
    ],
    [
      "index",
      "materials_job",
      "job_materials",
      "CREATE INDEX materials_job ON job_materials(job_id)"
    ],
    [
      "index",
      "photo_pair_phase",
      "job_photos",
      "CREATE UNIQUE INDEX photo_pair_phase ON job_photos(job_id,pair_id,phase) WHERE pair_id<>'' AND archived=0"
    ],
    [
      "index",
      "quote_current",
      "quote_links",
      "CREATE UNIQUE INDEX quote_current ON quote_links(root_id) WHERE current=1"
    ],
    [
      "index",
      "staff_updates_job",
      "staff_updates",
      "CREATE INDEX staff_updates_job ON staff_updates(job_id)"
    ],
    [
      "index",
      "time_job",
      "time_entries",
      "CREATE INDEX time_job ON time_entries(job_id)"
    ],
    [
      "index",
      "time_person_date",
      "time_entries",
      "CREATE INDEX time_person_date ON time_entries(resource_id,work_date)"
    ],
    [
      "table",
      "assets",
      "assets",
      "CREATE TABLE assets(id TEXT PRIMARY KEY, created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "audit",
      "audit",
      "CREATE TABLE audit(id INTEGER PRIMARY KEY AUTOINCREMENT, action TEXT NOT NULL, subject TEXT NOT NULL, created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "calendar_audit",
      "calendar_audit",
      "CREATE TABLE calendar_audit(id INTEGER PRIMARY KEY AUTOINCREMENT,record_id TEXT NOT NULL,actor TEXT NOT NULL,\n action TEXT NOT NULL,before_value TEXT,after_value TEXT,created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "calendar_bookings",
      "calendar_bookings",
      "CREATE TABLE calendar_bookings(\n id TEXT PRIMARY KEY,kind TEXT NOT NULL CHECK(kind IN ('job','unavailable')),job_id TEXT REFERENCES jobs(id),\n title TEXT NOT NULL,resource_ids TEXT NOT NULL,resource_snapshot TEXT NOT NULL,whole_team INTEGER NOT NULL,\n all_day INTEGER NOT NULL,start_date TEXT NOT NULL,end_date TEXT NOT NULL,start_time TEXT NOT NULL,end_time TEXT NOT NULL,\n start_fold INTEGER,end_fold INTEGER,start_utc TEXT NOT NULL,end_utc TEXT NOT NULL,note TEXT NOT NULL,\n version INTEGER NOT NULL DEFAULT 1,archived INTEGER NOT NULL DEFAULT 0,created_at TEXT NOT NULL,updated_at TEXT NOT NULL)"
    ],
    [
      "table",
      "customer_changes",
      "customer_changes",
      "CREATE TABLE customer_changes(id INTEGER PRIMARY KEY AUTOINCREMENT,\n customer_id TEXT NOT NULL REFERENCES customers(id), before_value TEXT NOT NULL,\n after_value TEXT NOT NULL, actor TEXT NOT NULL, created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "customer_links",
      "customer_links",
      "CREATE TABLE customer_links(id INTEGER PRIMARY KEY AUTOINCREMENT,\n job_id TEXT NOT NULL REFERENCES jobs(id), from_customer_id TEXT NOT NULL REFERENCES customers(id),\n to_customer_id TEXT NOT NULL REFERENCES customers(id), actor TEXT NOT NULL, created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "customers",
      "customers",
      "CREATE TABLE customers(id TEXT PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL, phone TEXT NOT NULL, address TEXT NOT NULL, portal_hash TEXT, portal_version INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL, version INTEGER NOT NULL DEFAULT 1)"
    ],
    [
      "table",
      "document_counters",
      "document_counters",
      "CREATE TABLE document_counters(kind TEXT PRIMARY KEY, next_value INTEGER NOT NULL)"
    ],
    [
      "table",
      "document_events",
      "document_events",
      "CREATE TABLE document_events(id INTEGER PRIMARY KEY AUTOINCREMENT,\n kind TEXT NOT NULL, document_id TEXT NOT NULL, action TEXT NOT NULL, before_value TEXT,\n after_value TEXT NOT NULL, reason TEXT NOT NULL, actor TEXT NOT NULL, created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "gallery_history",
      "gallery_history",
      "CREATE TABLE gallery_history(id INTEGER PRIMARY KEY AUTOINCREMENT,project_id TEXT NOT NULL REFERENCES gallery_projects(id),action TEXT NOT NULL,actor_id TEXT NOT NULL,membership_id TEXT NOT NULL,version INTEGER NOT NULL,snapshot TEXT NOT NULL,permission_confirmed INTEGER NOT NULL DEFAULT 0 CHECK(permission_confirmed IN (0,1)),created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "gallery_projects",
      "gallery_projects",
      "CREATE TABLE gallery_projects(id TEXT PRIMARY KEY,source_job_id TEXT UNIQUE REFERENCES jobs(id),draft_slug TEXT UNIQUE NOT NULL,draft TEXT NOT NULL,published TEXT,archived INTEGER NOT NULL DEFAULT 0 CHECK(archived IN (0,1)),version INTEGER NOT NULL DEFAULT 1,created_at TEXT NOT NULL,updated_at TEXT NOT NULL)"
    ],
    [
      "table",
      "invoices",
      "invoices",
      "CREATE TABLE \"invoices\"(id TEXT PRIMARY KEY, reference TEXT UNIQUE NOT NULL,\n quote_id TEXT REFERENCES quotes(id), job_id TEXT NOT NULL REFERENCES jobs(id),\n customer_id TEXT NOT NULL REFERENCES customers(id), snapshot TEXT NOT NULL,\n total_pence INTEGER NOT NULL CHECK(total_pence>0), created_at TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'draft' CHECK(status IN ('draft','issued','void','cancelled')),\n version INTEGER NOT NULL DEFAULT 1, issued_at TEXT,\n replaces_id TEXT UNIQUE REFERENCES invoices(id))"
    ],
    [
      "table",
      "job_materials",
      "job_materials",
      "CREATE TABLE job_materials(\n id TEXT PRIMARY KEY, job_id TEXT NOT NULL REFERENCES jobs(id),\n name TEXT NOT NULL, quantity TEXT NOT NULL, unit TEXT NOT NULL,\n cost_pence INTEGER NOT NULL CHECK(cost_pence >= 0), total_pence INTEGER NOT NULL CHECK(total_pence >= 0),\n supplier TEXT NOT NULL, supplier_sku TEXT NOT NULL, notes TEXT NOT NULL,\n status TEXT NOT NULL CHECK(status IN ('to_order','ordered','received')),\n ordered_date TEXT NOT NULL, received_date TEXT NOT NULL, supplier_links TEXT NOT NULL,\n catalogue_id TEXT REFERENCES materials_catalogue(id), catalogue_snapshot TEXT,\n archived INTEGER NOT NULL DEFAULT 0, version INTEGER NOT NULL DEFAULT 1,\n created_at TEXT NOT NULL, updated_at TEXT NOT NULL)"
    ],
    [
      "table",
      "job_photos",
      "job_photos",
      "CREATE TABLE job_photos(\n id TEXT PRIMARY KEY,job_id TEXT NOT NULL REFERENCES jobs(id),customer_id TEXT NOT NULL REFERENCES customers(id),blob_id TEXT UNIQUE NOT NULL REFERENCES photo_blobs(id),\n uploaded_by TEXT NOT NULL,membership_id TEXT NOT NULL,caption TEXT NOT NULL DEFAULT '',description TEXT NOT NULL DEFAULT '',category TEXT NOT NULL DEFAULT 'general',phase TEXT NOT NULL DEFAULT 'general',pair_id TEXT NOT NULL DEFAULT '',position INTEGER NOT NULL DEFAULT 0,\n reviewed INTEGER NOT NULL DEFAULT 0 CHECK(reviewed IN (0,1)),customer_share TEXT,archived INTEGER NOT NULL DEFAULT 0 CHECK(archived IN (0,1)),version INTEGER NOT NULL DEFAULT 1,created_at TEXT NOT NULL,updated_at TEXT NOT NULL)"
    ],
    [
      "table",
      "jobs",
      "jobs",
      "CREATE TABLE jobs(id TEXT PRIMARY KEY, customer_id TEXT NOT NULL REFERENCES customers(id), service TEXT NOT NULL, message TEXT NOT NULL, status TEXT NOT NULL, notes TEXT NOT NULL DEFAULT '', scheduled_date TEXT NOT NULL DEFAULT '', version INTEGER NOT NULL DEFAULT 1, source TEXT NOT NULL, created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "material_audit",
      "material_audit",
      "CREATE TABLE material_audit(\n id INTEGER PRIMARY KEY AUTOINCREMENT, record_id TEXT NOT NULL, kind TEXT NOT NULL,\n action TEXT NOT NULL, actor TEXT NOT NULL, version INTEGER NOT NULL,\n before_value TEXT, after_value TEXT, created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "materials_catalogue",
      "materials_catalogue",
      "CREATE TABLE materials_catalogue(\n id TEXT PRIMARY KEY, name TEXT NOT NULL, name_key TEXT NOT NULL,\n supplier TEXT NOT NULL, supplier_sku TEXT NOT NULL, unit TEXT NOT NULL,\n default_cost_pence INTEGER NOT NULL CHECK(default_cost_pence >= 0),\n default_charge_pence INTEGER CHECK(default_charge_pence >= 0),\n archived INTEGER NOT NULL DEFAULT 0, version INTEGER NOT NULL DEFAULT 1,\n created_at TEXT NOT NULL, updated_at TEXT NOT NULL)"
    ],
    [
      "table",
      "operations",
      "operations",
      "CREATE TABLE operations(key TEXT PRIMARY KEY, fingerprint TEXT NOT NULL, result TEXT NOT NULL)"
    ],
    [
      "table",
      "payment_allocations",
      "payment_allocations",
      "CREATE TABLE payment_allocations(payment_id TEXT NOT NULL REFERENCES payments(id),\n stage_id TEXT NOT NULL REFERENCES payment_stages(id), amount_pence INTEGER NOT NULL CHECK(amount_pence>0),\n PRIMARY KEY(payment_id,stage_id))"
    ],
    [
      "table",
      "payment_details",
      "payment_details",
      "CREATE TABLE payment_details(payment_id TEXT PRIMARY KEY REFERENCES payments(id),\n paid_on TEXT NOT NULL, method TEXT NOT NULL)"
    ],
    [
      "table",
      "payment_reversals",
      "payment_reversals",
      "CREATE TABLE payment_reversals(payment_id TEXT PRIMARY KEY REFERENCES payments(id),\n reason TEXT NOT NULL, actor TEXT NOT NULL, created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "payment_stages",
      "payment_stages",
      "CREATE TABLE payment_stages(id TEXT PRIMARY KEY, invoice_id TEXT NOT NULL REFERENCES invoices(id),\n position INTEGER NOT NULL, label TEXT NOT NULL, amount_pence INTEGER NOT NULL CHECK(amount_pence>0),\n due_date TEXT NOT NULL, requested_at TEXT, UNIQUE(invoice_id,position))"
    ],
    [
      "table",
      "payments",
      "payments",
      "CREATE TABLE payments(id TEXT PRIMARY KEY, invoice_id TEXT NOT NULL REFERENCES invoices(id), amount_pence INTEGER NOT NULL CHECK(amount_pence > 0), note TEXT NOT NULL, created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "photo_blobs",
      "photo_blobs",
      "CREATE TABLE photo_blobs(id TEXT PRIMARY KEY,sha256 TEXT NOT NULL,bytes INTEGER NOT NULL,width INTEGER NOT NULL,height INTEGER NOT NULL,created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "photo_history",
      "photo_history",
      "CREATE TABLE photo_history(id INTEGER PRIMARY KEY AUTOINCREMENT,photo_id TEXT NOT NULL REFERENCES job_photos(id),action TEXT NOT NULL,actor_id TEXT NOT NULL,membership_id TEXT NOT NULL,snapshot TEXT NOT NULL,created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "quote_links",
      "quote_links",
      "CREATE TABLE quote_links(quote_id TEXT PRIMARY KEY REFERENCES quotes(id),\n root_id TEXT NOT NULL REFERENCES quotes(id), previous_id TEXT UNIQUE REFERENCES quotes(id),\n revision INTEGER NOT NULL, current INTEGER NOT NULL CHECK(current IN (0,1)), shared_at TEXT, accepted_at TEXT)"
    ],
    [
      "table",
      "quotes",
      "quotes",
      "CREATE TABLE quotes(id TEXT PRIMARY KEY, reference TEXT UNIQUE NOT NULL, job_id TEXT NOT NULL REFERENCES jobs(id), customer_id TEXT NOT NULL REFERENCES customers(id), status TEXT NOT NULL, snapshot TEXT NOT NULL, total_pence INTEGER NOT NULL, version INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "setup_progress",
      "setup_progress",
      "CREATE TABLE setup_progress(id INTEGER PRIMARY KEY CHECK(id=1),version INTEGER NOT NULL DEFAULT 1,reviewed_publication_id INTEGER,solo_confirmed INTEGER NOT NULL DEFAULT 0 CHECK(solo_confirmed IN (0,1)),guide_read INTEGER NOT NULL DEFAULT 0 CHECK(guide_read IN (0,1)))"
    ],
    [
      "table",
      "site",
      "site",
      "CREATE TABLE site(id INTEGER PRIMARY KEY CHECK(id=1), draft TEXT NOT NULL, published TEXT NOT NULL, version INTEGER NOT NULL, published_at TEXT NOT NULL)"
    ],
    [
      "table",
      "site_history",
      "site_history",
      "CREATE TABLE site_history(id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT NOT NULL, created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "staff_updates",
      "staff_updates",
      "CREATE TABLE staff_updates(id TEXT PRIMARY KEY,job_id TEXT NOT NULL REFERENCES jobs(id),membership_id TEXT NOT NULL,author_id TEXT NOT NULL,resource_id TEXT NOT NULL REFERENCES team_resources(id),note TEXT NOT NULL,created_at TEXT NOT NULL)"
    ],
    [
      "table",
      "storage_identity",
      "storage_identity",
      "CREATE TABLE storage_identity(id INTEGER PRIMARY KEY CHECK(id=1), business_id TEXT NOT NULL, dataset_id TEXT NOT NULL)"
    ],
    [
      "table",
      "team_resources",
      "team_resources",
      "CREATE TABLE team_resources(\n id TEXT PRIMARY KEY,name TEXT NOT NULL,role TEXT NOT NULL,hourly_cost_pence INTEGER NOT NULL CHECK(hourly_cost_pence>=0),\n colour TEXT NOT NULL,version INTEGER NOT NULL DEFAULT 1,archived INTEGER NOT NULL DEFAULT 0,created_at TEXT NOT NULL,updated_at TEXT NOT NULL)"
    ],
    [
      "table",
      "time_entries",
      "time_entries",
      "CREATE TABLE time_entries(\n id TEXT PRIMARY KEY,job_id TEXT NOT NULL REFERENCES jobs(id),resource_id TEXT NOT NULL REFERENCES team_resources(id),\n resource_snapshot TEXT NOT NULL,work_date TEXT NOT NULL,duration_minutes INTEGER NOT NULL CHECK(duration_minutes>0 AND duration_minutes<=1440),\n hourly_cost_pence INTEGER NOT NULL CHECK(hourly_cost_pence>=0),cost_pence INTEGER NOT NULL CHECK(cost_pence>=0),note TEXT NOT NULL,\n version INTEGER NOT NULL DEFAULT 1,archived INTEGER NOT NULL DEFAULT 0,created_at TEXT NOT NULL,updated_at TEXT NOT NULL)"
    ],
    [
      "table",
      "workforce_audit",
      "workforce_audit",
      "CREATE TABLE workforce_audit(id INTEGER PRIMARY KEY AUTOINCREMENT,kind TEXT NOT NULL,record_id TEXT NOT NULL,\n actor TEXT NOT NULL,action TEXT NOT NULL,before_value TEXT,after_value TEXT,created_at TEXT NOT NULL)"
    ]
  ]
}
