CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, name TEXT NOT NULL, role TEXT NOT NULL CHECK (role IN ('tenant_admin', 'manager', 'technician')), active BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS clients ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, nif TEXT, phone TEXT, email TEXT, address TEXT, notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS vehicles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), client_id UUID REFERENCES clients(id) ON DELETE SET NULL, plate TEXT NOT NULL, brand TEXT NOT NULL, model TEXT NOT NULL, year INT, vin TEXT, mileage INT, notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS staff ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) ON DELETE SET NULL, name TEXT NOT NULL, email TEXT, phone TEXT, type TEXT NOT NULL CHECK (type IN ('internal', 'external')), hourly_rate NUMERIC(10,2) NOT NULL DEFAULT 0, active BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS catalog_items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), code TEXT NOT NULL UNIQUE, name TEXT NOT NULL, category TEXT NOT NULL, unit TEXT NOT NULL CHECK (unit IN ('un', 'hora', 'litro', 'kg')), base_price NUMERIC(10,2) NOT NULL DEFAULT 0, active BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS work_orders ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), number SERIAL UNIQUE, client_id UUID REFERENCES clients(id) ON DELETE SET NULL, vehicle_id UUID REFERENCES vehicles(id) ON DELETE SET NULL, status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'in_progress', 'completed', 'invoiced', 'cancelled')), internal_notes TEXT, client_notes TEXT, created_by UUID REFERENCES users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS wo_items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), work_order_id UUID NOT NULL REFERENCES work_orders(id) ON DELETE CASCADE, catalog_item_id UUID REFERENCES catalog_items(id) ON DELETE SET NULL, description TEXT NOT NULL, qty NUMERIC(10,3) NOT NULL DEFAULT 1, unit_price NUMERIC(10,2) NOT NULL, discount_pct NUMERIC(5,2) NOT NULL DEFAULT 0, total NUMERIC(10,2) GENERATED ALWAYS AS (qty * unit_price * (1 - discount_pct / 100)) STORED ); CREATE TABLE IF NOT EXISTS wo_staff_hours ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), work_order_id UUID NOT NULL REFERENCES work_orders(id) ON DELETE CASCADE, staff_id UUID NOT NULL REFERENCES staff(id) ON DELETE RESTRICT, hours NUMERIC(6,2) NOT NULL, cost_per_hour NUMERIC(10,2) NOT NULL, total NUMERIC(10,2) GENERATED ALWAYS AS (hours * cost_per_hour) STORED ); CREATE TABLE IF NOT EXISTS wo_status_log ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), work_order_id UUID NOT NULL REFERENCES work_orders(id) ON DELETE CASCADE, from_status TEXT, to_status TEXT NOT NULL, changed_by UUID REFERENCES users(id) ON DELETE SET NULL, changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS invoices ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), work_order_id UUID NOT NULL REFERENCES work_orders(id) ON DELETE RESTRICT, type TEXT NOT NULL CHECK (type IN ('quote', 'invoice')), number SERIAL UNIQUE, pdf_path TEXT, issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS expenses ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), vehicle_id UUID REFERENCES vehicles(id) ON DELETE SET NULL, type TEXT NOT NULL CHECK (type IN ('fuel', 'parts', 'tools', 'other')), amount NUMERIC(10,2) NOT NULL, description TEXT, date DATE NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS tenant_settings ( key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_work_orders_status ON work_orders(status); CREATE INDEX IF NOT EXISTS idx_work_orders_client_id ON work_orders(client_id); CREATE INDEX IF NOT EXISTS idx_vehicles_client_id ON vehicles(client_id); CREATE INDEX IF NOT EXISTS idx_vehicles_plate ON vehicles(plate); CREATE INDEX IF NOT EXISTS idx_expenses_date ON expenses(date);