1054 lines
45 KiB
PL/PgSQL
1054 lines
45 KiB
PL/PgSQL
-- ============================================================================
|
|
-- Nxtgauge Database — Complete Schema
|
|
-- Source: nxtgauge_database_source_of_truth.md
|
|
-- No duplicates. Every domain table from the document included.
|
|
-- ============================================================================
|
|
|
|
BEGIN;
|
|
|
|
-- ============================================================================
|
|
-- 1. IDENTITY & ACCESS CONTROL (users, roles, permissions, sessions, tokens)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS roles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
key VARCHAR(255) UNIQUE NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
audience VARCHAR(50) NOT NULL,
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS internal_roles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
role_id UUID NOT NULL UNIQUE REFERENCES roles(id) ON DELETE CASCADE,
|
|
description TEXT,
|
|
department_id UUID,
|
|
can_approve_requests BOOLEAN NOT NULL DEFAULT false,
|
|
can_manage_system_settings BOOLEAN NOT NULL DEFAULT false
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS external_roles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
role_id UUID NOT NULL UNIQUE REFERENCES roles(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS permissions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
key VARCHAR(100) UNIQUE NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
module VARCHAR(100),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS role_permissions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
|
|
permission_key VARCHAR(100) NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE(role_id, permission_key)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
email VARCHAR(255) UNIQUE NOT NULL,
|
|
phone VARCHAR(20) UNIQUE,
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
first_name VARCHAR(100),
|
|
last_name VARCHAR(100),
|
|
account_type TEXT DEFAULT 'INDIVIDUAL',
|
|
status VARCHAR(50) NOT NULL DEFAULT 'ACTIVE',
|
|
email_verified BOOLEAN NOT NULL DEFAULT false,
|
|
phone_verified BOOLEAN NOT NULL DEFAULT false,
|
|
role_id UUID REFERENCES roles(id) ON DELETE SET NULL,
|
|
last_login_at TIMESTAMPTZ,
|
|
email_verification_token VARCHAR(255),
|
|
email_verification_expires_at TIMESTAMPTZ,
|
|
reset_password_token VARCHAR(255),
|
|
reset_password_expires_at TIMESTAMPTZ,
|
|
deleted_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS refresh_tokens (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
token_hash VARCHAR(255) UNIQUE NOT NULL,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
revoked BOOLEAN NOT NULL DEFAULT false,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_hash ON refresh_tokens(token_hash);
|
|
|
|
CREATE TABLE IF NOT EXISTS user_sessions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
session_token TEXT UNIQUE NOT NULL,
|
|
ip_address TEXT,
|
|
user_agent TEXT,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_sessions_user_id ON user_sessions(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_sessions_token ON user_sessions(session_token);
|
|
|
|
CREATE TABLE IF NOT EXISTS user_roles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
|
|
approved_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE(user_id, role_id)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_roles_user_id ON user_roles(user_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS user_settings (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
settings JSONB NOT NULL DEFAULT '{}',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- 2. USER ROLE PROFILES (users can have multiple role profiles)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS user_role_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
role_key VARCHAR(50) NOT NULL,
|
|
display_name TEXT,
|
|
bio TEXT,
|
|
location TEXT,
|
|
avatar_url TEXT,
|
|
phone TEXT,
|
|
email TEXT,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'ACTIVE',
|
|
verification_status VARCHAR(50) DEFAULT 'PENDING',
|
|
approval_status VARCHAR(50) DEFAULT 'PENDING',
|
|
rejection_reason TEXT,
|
|
approved_at TIMESTAMPTZ,
|
|
verified_at TIMESTAMPTZ,
|
|
is_profile_public BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE(user_id, role_key)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_user_role_profiles_user_id ON user_role_profiles(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_role_profiles_role_key ON user_role_profiles(role_key);
|
|
|
|
-- ============================================================================
|
|
-- 3. ROLE EXTENSION TABLES (10 profession profiles)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS photographer_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
user_role_profile_id UUID REFERENCES user_role_profiles(id) ON DELETE SET NULL,
|
|
display_name VARCHAR(255), bio TEXT, location VARCHAR(255),
|
|
specialties TEXT[] DEFAULT '{}', camera_brands TEXT[] DEFAULT '{}',
|
|
studio_available BOOLEAN NOT NULL DEFAULT false, outdoor_shoots BOOLEAN NOT NULL DEFAULT true,
|
|
travel_radius_km INTEGER DEFAULT 50, starting_price_inr INTEGER DEFAULT 0,
|
|
custom_data JSONB,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING', rejection_reason TEXT, approved_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS tutor_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
user_role_profile_id UUID REFERENCES user_role_profiles(id) ON DELETE SET NULL,
|
|
display_name VARCHAR(255), bio TEXT, location VARCHAR(255),
|
|
subjects TEXT[] DEFAULT '{}', board_types TEXT[] DEFAULT '{}',
|
|
qualification VARCHAR(255), teaches_online BOOLEAN NOT NULL DEFAULT true,
|
|
teaches_offline BOOLEAN NOT NULL DEFAULT true, experience_years INTEGER DEFAULT 0,
|
|
hourly_rate_inr INTEGER DEFAULT 0, custom_data JSONB,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING', rejection_reason TEXT, approved_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS makeup_artist_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
user_role_profile_id UUID REFERENCES user_role_profiles(id) ON DELETE SET NULL,
|
|
display_name VARCHAR(255), bio TEXT, location VARCHAR(255),
|
|
specializations TEXT[] DEFAULT '{}', kit_brands TEXT[] DEFAULT '{}',
|
|
home_service BOOLEAN NOT NULL DEFAULT true, studio_available BOOLEAN NOT NULL DEFAULT false,
|
|
starting_price_inr INTEGER DEFAULT 0, custom_data JSONB,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING', rejection_reason TEXT, approved_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS developer_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
user_role_profile_id UUID REFERENCES user_role_profiles(id) ON DELETE SET NULL,
|
|
display_name VARCHAR(255), bio TEXT, location VARCHAR(255),
|
|
tech_stack TEXT[] DEFAULT '{}', experience_years INTEGER DEFAULT 0,
|
|
availability VARCHAR(50) DEFAULT 'FULL_TIME', hourly_rate_inr INTEGER DEFAULT 0,
|
|
remote_ok BOOLEAN NOT NULL DEFAULT true, custom_data JSONB,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING', rejection_reason TEXT, approved_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS video_editor_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
user_role_profile_id UUID REFERENCES user_role_profiles(id) ON DELETE SET NULL,
|
|
display_name VARCHAR(255), bio TEXT, location VARCHAR(255),
|
|
software_skills TEXT[] DEFAULT '{}', style_tags TEXT[] DEFAULT '{}',
|
|
turnaround_days INTEGER DEFAULT 7, starting_price_inr INTEGER DEFAULT 0,
|
|
custom_data JSONB,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING', rejection_reason TEXT, approved_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS graphic_designer_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
user_role_profile_id UUID REFERENCES user_role_profiles(id) ON DELETE SET NULL,
|
|
display_name VARCHAR(255), bio TEXT, location VARCHAR(255),
|
|
design_tools TEXT[] DEFAULT '{}', style_tags TEXT[] DEFAULT '{}',
|
|
brand_experience BOOLEAN NOT NULL DEFAULT false, starting_price_inr INTEGER DEFAULT 0,
|
|
custom_data JSONB,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING', rejection_reason TEXT, approved_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS social_media_manager_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
user_role_profile_id UUID REFERENCES user_role_profiles(id) ON DELETE SET NULL,
|
|
display_name VARCHAR(255), bio TEXT, location VARCHAR(255),
|
|
platforms TEXT[] DEFAULT '{}', industries TEXT[] DEFAULT '{}',
|
|
content_types TEXT[] DEFAULT '{}', avg_follower_growth_pct INTEGER DEFAULT 0,
|
|
starting_price_inr INTEGER DEFAULT 0, custom_data JSONB,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING', rejection_reason TEXT, approved_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS fitness_trainer_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
user_role_profile_id UUID REFERENCES user_role_profiles(id) ON DELETE SET NULL,
|
|
display_name VARCHAR(255), bio TEXT, location VARCHAR(255),
|
|
disciplines TEXT[] DEFAULT '{}', certifications TEXT[] DEFAULT '{}',
|
|
online_sessions BOOLEAN NOT NULL DEFAULT true, home_visits BOOLEAN NOT NULL DEFAULT false,
|
|
gym_based BOOLEAN NOT NULL DEFAULT false, per_session_rate_inr INTEGER DEFAULT 0,
|
|
custom_data JSONB,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING', rejection_reason TEXT, approved_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS catering_service_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
user_role_profile_id UUID REFERENCES user_role_profiles(id) ON DELETE SET NULL,
|
|
business_name VARCHAR(255), bio TEXT, location VARCHAR(255),
|
|
cuisine_types TEXT[] DEFAULT '{}', event_types TEXT[] DEFAULT '{}',
|
|
min_guests INTEGER DEFAULT 10, max_guests INTEGER DEFAULT 500,
|
|
has_setup_team BOOLEAN NOT NULL DEFAULT true, has_serving_staff BOOLEAN NOT NULL DEFAULT true,
|
|
price_per_head_inr INTEGER DEFAULT 0, custom_data JSONB,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING', rejection_reason TEXT, approved_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS ugc_content_creator_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
user_role_profile_id UUID REFERENCES user_role_profiles(id) ON DELETE SET NULL,
|
|
display_name VARCHAR(255), bio TEXT, location VARCHAR(255),
|
|
platforms TEXT[] DEFAULT '{}', content_niches TEXT[] DEFAULT '{}',
|
|
content_formats TEXT[] DEFAULT '{}', follower_count INTEGER DEFAULT 0,
|
|
avg_views_per_post INTEGER DEFAULT 0, has_media_kit BOOLEAN NOT NULL DEFAULT false,
|
|
instagram_handle VARCHAR(100), youtube_channel_url VARCHAR(500),
|
|
starting_price_inr INTEGER DEFAULT 0, custom_data JSONB,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING', rejection_reason TEXT, approved_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- 4. COMPANY PROFILES
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS company_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
company_name VARCHAR(255) NOT NULL,
|
|
business_type VARCHAR(100), registration_number VARCHAR(100),
|
|
industry VARCHAR(150), website_url VARCHAR(255), employee_count INT,
|
|
gst_number VARCHAR(50), contact_name VARCHAR(255),
|
|
contact_email VARCHAR(255), contact_phone VARCHAR(20),
|
|
address_line1 VARCHAR(500), city VARCHAR(100), state VARCHAR(100),
|
|
country VARCHAR(100) NOT NULL DEFAULT 'India', postal_code VARCHAR(20),
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
|
|
free_job_slots INTEGER NOT NULL DEFAULT 1,
|
|
purchased_job_slots INTEGER NOT NULL DEFAULT 0,
|
|
free_contact_views INTEGER NOT NULL DEFAULT 30,
|
|
purchased_contact_views INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- 5. CUSTOMER PROFILES
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS customer_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
full_name VARCHAR(255), bio TEXT, phone VARCHAR(20),
|
|
city VARCHAR(100), area VARCHAR(100), location VARCHAR(255),
|
|
preferred_professions TEXT[] DEFAULT '{}',
|
|
active_requirement_count INTEGER NOT NULL DEFAULT 0,
|
|
custom_data JSONB DEFAULT '{}'::jsonb,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'ACTIVE',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- 6. JOB SEEKER PROFILES
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS job_seeker_profiles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
full_name VARCHAR(255), bio TEXT, location VARCHAR(255), summary TEXT,
|
|
experience_years INTEGER DEFAULT 0, skills TEXT[] DEFAULT '{}',
|
|
resume_url VARCHAR(500), active_application_count INTEGER NOT NULL DEFAULT 0,
|
|
custom_data JSONB DEFAULT '{}'::jsonb,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'ACTIVE',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- 7. PORTFOLIO DOMAIN (native content only, no external links)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS portfolio_items (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
user_role_profile_id UUID REFERENCES user_role_profiles(id) ON DELETE CASCADE,
|
|
title VARCHAR(255) NOT NULL, description TEXT,
|
|
tags TEXT[] DEFAULT '{}', display_order INTEGER DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_portfolio_items_user_id ON portfolio_items(user_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS portfolio_images (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
portfolio_item_id UUID NOT NULL REFERENCES portfolio_items(id) ON DELETE CASCADE,
|
|
file_url VARCHAR(500) NOT NULL, display_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS services (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
user_role_profile_id UUID REFERENCES user_role_profiles(id) ON DELETE CASCADE,
|
|
name VARCHAR(255) NOT NULL, description TEXT,
|
|
price INTEGER NOT NULL DEFAULT 0, duration_minutes INTEGER,
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- 8. VERIFICATION DOMAIN
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS verification_requests (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_role_profile_id UUID REFERENCES user_role_profiles(id) ON DELETE CASCADE,
|
|
verification_type VARCHAR(50) NOT NULL,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
|
|
submitted_at TIMESTAMPTZ,
|
|
reviewed_at TIMESTAMPTZ,
|
|
reviewed_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
remarks TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_verification_requests_status ON verification_requests(status);
|
|
|
|
CREATE TABLE IF NOT EXISTS verification_documents (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
verification_request_id UUID NOT NULL REFERENCES verification_requests(id) ON DELETE CASCADE,
|
|
document_type VARCHAR(100) NOT NULL,
|
|
file_url VARCHAR(500) NOT NULL,
|
|
file_name VARCHAR(255),
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
|
|
uploaded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
reviewed_at TIMESTAMPTZ,
|
|
reviewed_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
remarks TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS verification_logs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
verification_request_id UUID NOT NULL REFERENCES verification_requests(id) ON DELETE CASCADE,
|
|
action VARCHAR(50) NOT NULL,
|
|
old_status VARCHAR(50),
|
|
new_status VARCHAR(50),
|
|
acted_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
remarks TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- verifications table used by Rust code
|
|
CREATE TABLE IF NOT EXISTS verifications (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
role_key VARCHAR(50) NOT NULL,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
|
|
priority VARCHAR(10) NOT NULL DEFAULT 'LOW',
|
|
case_type VARCHAR(50) NOT NULL,
|
|
payload JSONB NOT NULL DEFAULT '{}',
|
|
documents JSONB NOT NULL DEFAULT '[]',
|
|
notes TEXT,
|
|
rejection_reason TEXT,
|
|
assigned_to UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_verifications_user_id ON verifications(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_verifications_status ON verifications(status);
|
|
CREATE INDEX IF NOT EXISTS idx_verifications_case_type ON verifications(case_type);
|
|
|
|
-- ============================================================================
|
|
-- 9. APPROVAL DOMAIN
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS approval_requests (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
entity_type VARCHAR(50) NOT NULL,
|
|
entity_id UUID NOT NULL,
|
|
approval_type VARCHAR(50) NOT NULL,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
|
|
submitted_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
reviewed_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
submitted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
reviewed_at TIMESTAMPTZ,
|
|
remarks TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_approval_requests_status ON approval_requests(status);
|
|
CREATE INDEX IF NOT EXISTS idx_approval_requests_entity ON approval_requests(entity_type, entity_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS approval_logs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
approval_request_id UUID NOT NULL REFERENCES approval_requests(id) ON DELETE CASCADE,
|
|
action VARCHAR(50) NOT NULL,
|
|
old_status VARCHAR(50),
|
|
new_status VARCHAR(50),
|
|
acted_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
remarks TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- 10. MARKETPLACE DOMAIN (jobs, leads, reviews)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS jobs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
company_id UUID NOT NULL REFERENCES company_profiles(id) ON DELETE CASCADE,
|
|
title VARCHAR(200) NOT NULL, category VARCHAR(100),
|
|
description TEXT NOT NULL, location VARCHAR(255) NOT NULL,
|
|
job_type VARCHAR(50) NOT NULL DEFAULT 'FULL_TIME',
|
|
mode_of_work VARCHAR(50),
|
|
salary_min INTEGER, salary_max INTEGER, budget_inr INTEGER,
|
|
experience_years INTEGER, skills TEXT[] DEFAULT '{}',
|
|
posted_by_user_id UUID REFERENCES users(id),
|
|
status VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
|
|
rejection_reason TEXT, expires_at TIMESTAMPTZ,
|
|
approved_at TIMESTAMPTZ, approved_by UUID REFERENCES users(id),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_jobs_company_id ON jobs(company_id);
|
|
CREATE INDEX IF NOT EXISTS idx_jobs_status ON jobs(status);
|
|
|
|
CREATE TABLE IF NOT EXISTS job_applications (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
job_id UUID NOT NULL REFERENCES jobs(id) ON DELETE CASCADE,
|
|
applicant_user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
cover_note TEXT,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'APPLIED',
|
|
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_job_applications_job_id ON job_applications(job_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS leads (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
profession_key VARCHAR(50) NOT NULL,
|
|
title VARCHAR(200) NOT NULL, description TEXT NOT NULL,
|
|
location VARCHAR(255) NOT NULL, budget_inr INTEGER,
|
|
required_date DATE, extra_data_json JSONB,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
|
|
rejection_reason TEXT,
|
|
request_count INTEGER NOT NULL DEFAULT 0,
|
|
accepted_count INTEGER NOT NULL DEFAULT 0,
|
|
expires_at TIMESTAMPTZ,
|
|
approved_at TIMESTAMPTZ, approved_by UUID REFERENCES users(id),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_leads_status ON leads(status);
|
|
CREATE INDEX IF NOT EXISTS idx_leads_profession_key ON leads(profession_key);
|
|
|
|
CREATE TABLE IF NOT EXISTS lead_requests (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
lead_id UUID REFERENCES leads(id) ON DELETE CASCADE,
|
|
user_role_profile_id UUID REFERENCES user_role_profiles(id) ON DELETE CASCADE,
|
|
professional_user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
|
|
tracecoins_reserved INTEGER NOT NULL DEFAULT 25,
|
|
remarks TEXT,
|
|
expires_at TIMESTAMPTZ NOT NULL DEFAULT (NOW() + INTERVAL '1 day'),
|
|
requested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
resolved_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_lead_requests_lead_id ON lead_requests(lead_id);
|
|
CREATE INDEX IF NOT EXISTS idx_lead_requests_status ON lead_requests(status);
|
|
|
|
CREATE TABLE IF NOT EXISTS reviews (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
reviewer_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
entity_type VARCHAR(50) NOT NULL DEFAULT 'PLATFORM',
|
|
entity_id VARCHAR(255),
|
|
title VARCHAR(255),
|
|
rating SMALLINT CHECK (rating >= 1 AND rating <= 5),
|
|
review_text TEXT,
|
|
reviewer_name VARCHAR(255),
|
|
subject_type VARCHAR(50) NOT NULL DEFAULT 'PLATFORM',
|
|
subject_id VARCHAR(255),
|
|
status VARCHAR(20) NOT NULL DEFAULT 'PUBLISHED',
|
|
is_published BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- 11. FINANCE DOMAIN (wallets, ledger, pricing, payments, invoices, orders)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS tracecoin_wallets (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
balance INTEGER NOT NULL DEFAULT 0,
|
|
reserved INTEGER NOT NULL DEFAULT 0,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS tracecoin_ledger (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
wallet_id UUID NOT NULL REFERENCES tracecoin_wallets(id),
|
|
amount INTEGER NOT NULL,
|
|
transaction_type VARCHAR(20) NOT NULL,
|
|
reference_type VARCHAR(100),
|
|
reference_id UUID,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_tracecoin_ledger_wallet_id ON tracecoin_ledger(wallet_id);
|
|
|
|
CREATE OR REPLACE FUNCTION prevent_tracecoin_ledger_mutation()
|
|
RETURNS trigger AS $$
|
|
BEGIN
|
|
RAISE EXCEPTION 'tracecoin_ledger is immutable; % is not allowed', TG_OP;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DROP TRIGGER IF EXISTS trg_prevent_tracecoin_ledger_update ON tracecoin_ledger;
|
|
CREATE TRIGGER trg_prevent_tracecoin_ledger_update
|
|
BEFORE UPDATE ON tracecoin_ledger FOR EACH ROW
|
|
EXECUTE FUNCTION prevent_tracecoin_ledger_mutation();
|
|
|
|
DROP TRIGGER IF EXISTS trg_prevent_tracecoin_ledger_delete ON tracecoin_ledger;
|
|
CREATE TRIGGER trg_prevent_tracecoin_ledger_delete
|
|
BEFORE DELETE ON tracecoin_ledger FOR EACH ROW
|
|
EXECUTE FUNCTION prevent_tracecoin_ledger_mutation();
|
|
|
|
CREATE TABLE IF NOT EXISTS pricing_packages (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name VARCHAR(255) NOT NULL,
|
|
role_key VARCHAR(50) NOT NULL,
|
|
package_type VARCHAR(50) NOT NULL,
|
|
tracecoins_amount INTEGER NOT NULL DEFAULT 0,
|
|
price_inr INTEGER NOT NULL,
|
|
description TEXT,
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS payments (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id),
|
|
package_id UUID NOT NULL REFERENCES pricing_packages(id),
|
|
razorpay_order_id VARCHAR(100),
|
|
razorpay_payment_id VARCHAR(100),
|
|
amount_inr INTEGER NOT NULL,
|
|
tracecoins_credited INTEGER NOT NULL DEFAULT 0,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
|
|
verified_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_payments_user_id ON payments(user_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS invoices (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
payment_id UUID NOT NULL REFERENCES payments(id),
|
|
user_id UUID NOT NULL REFERENCES users(id),
|
|
invoice_number VARCHAR(50) NOT NULL UNIQUE,
|
|
subtotal INTEGER NOT NULL,
|
|
gst_amount INTEGER NOT NULL,
|
|
total INTEGER NOT NULL,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'ISSUED',
|
|
issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
file_url VARCHAR(500)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_invoices_user_id ON invoices(user_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS orders (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
|
|
total_amount INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS order_items (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
order_id UUID NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
|
|
package_id UUID REFERENCES pricing_packages(id),
|
|
item_type VARCHAR(50) NOT NULL,
|
|
quantity INTEGER NOT NULL DEFAULT 1,
|
|
unit_price INTEGER NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS tax_rules (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
title VARCHAR(255) NOT NULL,
|
|
tax_type VARCHAR(50) NOT NULL,
|
|
percentage DECIMAL(5,2) NOT NULL,
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
applicable_to TEXT[] DEFAULT '{}',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS discounts (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
title VARCHAR(255) NOT NULL,
|
|
scope VARCHAR(20) NOT NULL DEFAULT 'ROLE',
|
|
role_key VARCHAR(50),
|
|
package_id UUID REFERENCES pricing_packages(id) ON DELETE SET NULL,
|
|
discount_type VARCHAR(20) NOT NULL,
|
|
discount_value INTEGER NOT NULL,
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS coupons (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
code VARCHAR(50) NOT NULL UNIQUE,
|
|
title VARCHAR(255),
|
|
description TEXT,
|
|
discount_type VARCHAR(20) NOT NULL,
|
|
discount_value INTEGER NOT NULL,
|
|
applies_to VARCHAR(50) NOT NULL DEFAULT 'ALL',
|
|
min_order_amount INTEGER NOT NULL DEFAULT 0,
|
|
max_uses INTEGER,
|
|
uses_count INTEGER NOT NULL DEFAULT 0,
|
|
per_user_limit INTEGER NOT NULL DEFAULT 1,
|
|
role_keys TEXT[] NOT NULL DEFAULT '{}',
|
|
valid_from TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
valid_until TIMESTAMPTZ,
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS coupon_uses (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
coupon_id UUID NOT NULL REFERENCES coupons(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
payment_id UUID REFERENCES payments(id),
|
|
used_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE(coupon_id, user_id)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_coupon_uses_user_id ON coupon_uses(user_id);
|
|
|
|
-- ============================================================================
|
|
-- 12. KNOWLEDGE BASE
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS kb_categories (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name VARCHAR(255) NOT NULL UNIQUE,
|
|
slug VARCHAR(255) NOT NULL UNIQUE,
|
|
description TEXT,
|
|
display_order INTEGER NOT NULL DEFAULT 0,
|
|
is_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 kb_sections (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
category_id UUID NOT NULL REFERENCES kb_categories(id) ON DELETE CASCADE,
|
|
title VARCHAR(255) NOT NULL,
|
|
slug VARCHAR(255) NOT NULL UNIQUE,
|
|
display_order INTEGER NOT NULL DEFAULT 0,
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS kb_articles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
category_id UUID NOT NULL REFERENCES kb_categories(id) ON DELETE CASCADE,
|
|
section_id UUID REFERENCES kb_sections(id),
|
|
title VARCHAR(500) NOT NULL,
|
|
slug VARCHAR(500) NOT NULL UNIQUE,
|
|
summary TEXT,
|
|
body TEXT NOT NULL,
|
|
content_markdown TEXT,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'DRAFT',
|
|
target_roles TEXT[] DEFAULT '{}',
|
|
tags TEXT[] DEFAULT '{}',
|
|
views INTEGER NOT NULL DEFAULT 0,
|
|
created_by UUID REFERENCES users(id),
|
|
published_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_kb_articles_category_id ON kb_articles(category_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS kb_article_feedback (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
article_id UUID NOT NULL REFERENCES kb_articles(id) ON DELETE CASCADE,
|
|
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
helpful BOOLEAN NOT NULL,
|
|
comment TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- 13. SUPPORT SYSTEM
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS support_tickets (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
subject VARCHAR(500) NOT NULL,
|
|
description TEXT,
|
|
category VARCHAR(50) NOT NULL DEFAULT 'GENERAL',
|
|
status VARCHAR(20) NOT NULL DEFAULT 'OPEN',
|
|
priority VARCHAR(10) NOT NULL DEFAULT 'NORMAL',
|
|
assigned_to UUID REFERENCES users(id),
|
|
requester_name VARCHAR(255),
|
|
requester_email VARCHAR(255),
|
|
resolved_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_support_tickets_user_id ON support_tickets(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_support_tickets_status ON support_tickets(status);
|
|
|
|
CREATE TABLE IF NOT EXISTS support_ticket_messages (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
ticket_id UUID NOT NULL REFERENCES support_tickets(id) ON DELETE CASCADE,
|
|
sender_id UUID NOT NULL REFERENCES users(id),
|
|
body TEXT NOT NULL,
|
|
is_internal BOOLEAN NOT NULL DEFAULT false,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_support_ticket_messages_ticket_id ON support_ticket_messages(ticket_id);
|
|
|
|
-- ============================================================================
|
|
-- 14. NOTIFICATION SYSTEM
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS notifications (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
title VARCHAR(255) NOT NULL,
|
|
body TEXT,
|
|
type VARCHAR(50),
|
|
reference_id UUID,
|
|
is_read BOOLEAN NOT NULL DEFAULT false,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_notifications_user_id ON notifications(user_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS notification_preferences (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
preferences JSONB NOT NULL DEFAULT '{}',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS notification_templates (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
trigger_event VARCHAR(100) NOT NULL UNIQUE,
|
|
title_template VARCHAR(500),
|
|
body_template TEXT,
|
|
channel VARCHAR(20) NOT NULL DEFAULT 'IN_APP',
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS email_logs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID REFERENCES users(id),
|
|
trigger VARCHAR(100) NOT NULL,
|
|
to_email VARCHAR(255) NOT NULL,
|
|
subject VARCHAR(500),
|
|
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
|
|
sent_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS smtp_configs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
host VARCHAR(255) NOT NULL,
|
|
port INTEGER NOT NULL DEFAULT 587,
|
|
username VARCHAR(255) NOT NULL,
|
|
password_encrypted TEXT NOT NULL,
|
|
from_email VARCHAR(255) NOT NULL,
|
|
from_name VARCHAR(255),
|
|
use_tls BOOLEAN NOT NULL DEFAULT true,
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- 15. DASHBOARD SYSTEM
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS dashboard_configs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
|
|
audience VARCHAR(50) NOT NULL,
|
|
config_json JSONB NOT NULL,
|
|
version INTEGER NOT NULL DEFAULT 1,
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_active_dashboard_per_role_audience
|
|
ON dashboard_configs(role_id, audience) WHERE is_active = true;
|
|
|
|
CREATE TABLE IF NOT EXISTS dashboard_widgets (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
dashboard_config_id UUID NOT NULL REFERENCES dashboard_configs(id) ON DELETE CASCADE,
|
|
widget_key VARCHAR(100) NOT NULL,
|
|
widget_type VARCHAR(50) NOT NULL,
|
|
title VARCHAR(255) NOT NULL,
|
|
config_json JSONB NOT NULL DEFAULT '{}',
|
|
display_order INTEGER NOT NULL DEFAULT 0,
|
|
is_visible BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS runtime_configs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
|
|
config_json JSONB NOT NULL,
|
|
version INTEGER NOT NULL DEFAULT 1,
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_active_runtime_per_role
|
|
ON runtime_configs(role_id) WHERE is_active = true;
|
|
|
|
-- ============================================================================
|
|
-- 16. ONBOARDING (deprecated but tables kept for backward compat)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS onboarding_configs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
|
|
schema_json JSONB NOT NULL,
|
|
version INTEGER NOT NULL DEFAULT 1,
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_active_onboarding_per_role
|
|
ON onboarding_configs(role_id) WHERE is_active = true;
|
|
|
|
CREATE TABLE IF NOT EXISTS onboarding_states (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'NOT_STARTED',
|
|
progress_json JSONB NOT NULL DEFAULT '{}',
|
|
completed_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_onboarding_state_user_role
|
|
ON onboarding_states(user_id, role_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS onboarding_submissions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
role_id UUID NOT NULL REFERENCES roles(id),
|
|
config_id UUID REFERENCES onboarding_configs(id),
|
|
data_json JSONB,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
|
|
submitted_at TIMESTAMPTZ,
|
|
reviewed_at TIMESTAMPTZ,
|
|
reviewed_by UUID REFERENCES users(id),
|
|
rejection_reason TEXT,
|
|
document_request TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_onboarding_submissions_user_id ON onboarding_submissions(user_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS submission_documents (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
submission_id UUID NOT NULL REFERENCES onboarding_submissions(id) ON DELETE CASCADE,
|
|
document_type VARCHAR(100) NOT NULL,
|
|
file_url VARCHAR(500) NOT NULL,
|
|
file_name VARCHAR(255),
|
|
uploaded_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- 17. INTERNAL EMPLOYEES
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS departments (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name VARCHAR(100) NOT NULL UNIQUE,
|
|
code VARCHAR(64),
|
|
description TEXT,
|
|
department_head VARCHAR(255),
|
|
department_email VARCHAR(255),
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_departments_code_unique
|
|
ON departments(LOWER(code)) WHERE code IS NOT NULL;
|
|
|
|
CREATE TABLE IF NOT EXISTS designations (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name VARCHAR(100) NOT NULL UNIQUE,
|
|
code VARCHAR(64),
|
|
department_id UUID REFERENCES departments(id) ON DELETE SET NULL,
|
|
description TEXT,
|
|
level VARCHAR(100),
|
|
can_manage_team BOOLEAN NOT NULL DEFAULT false,
|
|
can_approve BOOLEAN NOT NULL DEFAULT false,
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_designations_code_unique
|
|
ON designations(LOWER(code)) WHERE code IS NOT NULL;
|
|
|
|
CREATE TABLE IF NOT EXISTS employees (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
first_name VARCHAR(100) NOT NULL,
|
|
last_name VARCHAR(100) NOT NULL,
|
|
email VARCHAR(255) UNIQUE NOT NULL,
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
employee_code VARCHAR(50) UNIQUE,
|
|
department_id UUID REFERENCES departments(id) ON DELETE SET NULL,
|
|
designation_id UUID REFERENCES designations(id) ON DELETE SET NULL,
|
|
role_code VARCHAR(50) NOT NULL DEFAULT 'STAFF',
|
|
status VARCHAR(50) NOT NULL DEFAULT 'ACTIVE',
|
|
joining_date DATE DEFAULT CURRENT_DATE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_employees_email ON employees(email);
|
|
|
|
CREATE TABLE IF NOT EXISTS employee_sessions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
employee_id UUID NOT NULL REFERENCES employees(id) ON DELETE CASCADE,
|
|
token_hash VARCHAR(255) UNIQUE NOT NULL,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
revoked BOOLEAN NOT NULL DEFAULT false,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- 18. AUDIT MANAGEMENT
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS audit_logs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
actor_user_id UUID NOT NULL,
|
|
actor_type VARCHAR(20) NOT NULL,
|
|
action VARCHAR(100) NOT NULL,
|
|
entity_type VARCHAR(50) NOT NULL,
|
|
entity_id UUID NOT NULL,
|
|
module_key VARCHAR(100),
|
|
request_id UUID,
|
|
ip_address VARCHAR(45),
|
|
user_agent TEXT,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'SUCCESS',
|
|
summary TEXT,
|
|
metadata_json JSONB,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_entity ON audit_logs(entity_type, entity_id);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_actor ON audit_logs(actor_type, actor_user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_created_at ON audit_logs(created_at DESC);
|
|
|
|
CREATE TABLE IF NOT EXISTS audit_log_changes (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
audit_log_id UUID NOT NULL REFERENCES audit_logs(id) ON DELETE CASCADE,
|
|
field_name VARCHAR(255) NOT NULL,
|
|
old_value TEXT,
|
|
new_value TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- activity_logs used by Rust code
|
|
CREATE TABLE IF NOT EXISTS activity_logs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
actor_id UUID NOT NULL,
|
|
actor_type VARCHAR(20) NOT NULL,
|
|
entity_id UUID NOT NULL,
|
|
entity_type VARCHAR(50) NOT NULL,
|
|
action VARCHAR(100) NOT NULL,
|
|
metadata JSONB,
|
|
ip_address VARCHAR(45),
|
|
user_agent TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_activity_logs_entity ON activity_logs(entity_type, entity_id);
|
|
CREATE INDEX IF NOT EXISTS idx_activity_logs_created_at ON activity_logs(created_at DESC);
|
|
|
|
-- ============================================================================
|
|
-- 19. ACCOUNT DELETION
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS account_deletion_requests (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
reason TEXT,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
|
|
requested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
processed_at TIMESTAMPTZ,
|
|
processed_by UUID REFERENCES users(id)
|
|
);
|
|
|
|
COMMIT;
|