-- ============================================================ -- OPTIM AI BRE ENGINE - COMPLETE DATABASE SCHEMA -- PostgreSQL 15+ -- Multi-Tenant, Production-Grade -- ============================================================ -- Enable extensions CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS "pgcrypto"; CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- ============================================================ -- SCHEMA: TENANTS & MULTI-TENANCY -- ============================================================ CREATE TABLE tenants ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_code VARCHAR(50) UNIQUE NOT NULL, tenant_name VARCHAR(200) NOT NULL, display_name VARCHAR(200), logo_url TEXT, primary_color VARCHAR(10) DEFAULT '#1E40AF', secondary_color VARCHAR(10) DEFAULT '#3B82F6', plan_type VARCHAR(50) NOT NULL DEFAULT 'ENTERPRISE', -- STARTER, PROFESSIONAL, ENTERPRISE max_rules INTEGER NOT NULL DEFAULT 1000, max_executions_per_day BIGINT NOT NULL DEFAULT 1000000, is_active BOOLEAN NOT NULL DEFAULT TRUE, trial_end_date TIMESTAMPTZ, subscription_end_date TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID, settings JSONB NOT NULL DEFAULT '{}'::JSONB ); CREATE TABLE tenant_configurations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, config_key VARCHAR(200) NOT NULL, config_value TEXT, config_type VARCHAR(50) NOT NULL DEFAULT 'STRING', -- STRING, JSON, BOOLEAN, NUMBER category VARCHAR(100), is_encrypted BOOLEAN DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(tenant_id, config_key) ); -- ============================================================ -- SCHEMA: IDENTITY & ACCESS MANAGEMENT -- ============================================================ CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, email VARCHAR(320) NOT NULL, username VARCHAR(100) NOT NULL, password_hash TEXT NOT NULL, full_name VARCHAR(200) NOT NULL, employee_id VARCHAR(100), designation VARCHAR(200), department VARCHAR(200), mobile VARCHAR(20), profile_image_url TEXT, is_active BOOLEAN NOT NULL DEFAULT TRUE, is_email_verified BOOLEAN NOT NULL DEFAULT FALSE, is_mfa_enabled BOOLEAN NOT NULL DEFAULT FALSE, mfa_secret TEXT, last_login_at TIMESTAMPTZ, last_login_ip INET, failed_login_count INTEGER NOT NULL DEFAULT 0, locked_until TIMESTAMPTZ, password_changed_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(tenant_id, email), UNIQUE(tenant_id, username) ); CREATE TABLE roles ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, role_code VARCHAR(100) NOT NULL, role_name VARCHAR(200) NOT NULL, description TEXT, is_system_role 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(), UNIQUE(tenant_id, role_code) ); CREATE TABLE permissions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), permission_code VARCHAR(200) UNIQUE NOT NULL, permission_name VARCHAR(200) NOT NULL, module VARCHAR(100) NOT NULL, description TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE role_permissions ( role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE, permission_id UUID NOT NULL REFERENCES permissions(id) ON DELETE CASCADE, granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), granted_by UUID, PRIMARY KEY (role_id, permission_id) ); CREATE TABLE user_roles ( user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE, assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), assigned_by UUID, PRIMARY KEY (user_id, role_id) ); CREATE TABLE api_keys ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, key_name VARCHAR(200) NOT NULL, api_key_hash TEXT NOT NULL UNIQUE, api_key_prefix VARCHAR(20) NOT NULL, scopes TEXT[] NOT NULL DEFAULT '{}', is_active BOOLEAN NOT NULL DEFAULT TRUE, expires_at TIMESTAMPTZ, last_used_at TIMESTAMPTZ, rate_limit_per_minute INTEGER NOT NULL DEFAULT 1000, created_by UUID NOT NULL REFERENCES users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE refresh_tokens ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, token_hash TEXT NOT NULL UNIQUE, expires_at TIMESTAMPTZ NOT NULL, is_revoked BOOLEAN NOT NULL DEFAULT FALSE, ip_address INET, user_agent TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- ============================================================ -- SCHEMA: PRODUCT & BRANCH CONFIGURATION -- ============================================================ CREATE TABLE products ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, product_code VARCHAR(100) NOT NULL, product_name VARCHAR(200) NOT NULL, product_type VARCHAR(100) NOT NULL, -- VEHICLE_LOAN, TRACTOR_LOAN, AUTO_LOAN, CV_LOAN, MSME, HOME_LOAN, PERSONAL_LOAN description TEXT, is_active BOOLEAN NOT NULL DEFAULT TRUE, config JSONB NOT NULL DEFAULT '{}'::JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(tenant_id, product_code) ); CREATE TABLE branches ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, branch_code VARCHAR(100) NOT NULL, branch_name VARCHAR(200) NOT NULL, region VARCHAR(100), state VARCHAR(100), city VARCHAR(100), zone VARCHAR(100), is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(tenant_id, branch_code) ); CREATE TABLE loan_stages ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, stage_code VARCHAR(100) NOT NULL, stage_name VARCHAR(200) NOT NULL, stage_order INTEGER NOT NULL DEFAULT 0, description TEXT, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(tenant_id, stage_code) ); -- ============================================================ -- SCHEMA: RULE ENGINE CORE -- ============================================================ CREATE TABLE rule_categories ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, category_code VARCHAR(100) NOT NULL, category_name VARCHAR(200) NOT NULL, description TEXT, icon VARCHAR(100), sort_order INTEGER NOT NULL DEFAULT 0, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(tenant_id, category_code) ); CREATE TABLE rules ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, rule_code VARCHAR(200) NOT NULL, rule_name VARCHAR(500) NOT NULL, description TEXT, category_id UUID REFERENCES rule_categories(id), rule_type VARCHAR(100) NOT NULL, -- ELIGIBILITY, CREDIT, BUREAU, FI, VALUATION, FRAUD, COMPLIANCE, DEVIATION, INCOME, KYC priority INTEGER NOT NULL DEFAULT 100, is_active BOOLEAN NOT NULL DEFAULT TRUE, is_published BOOLEAN NOT NULL DEFAULT FALSE, is_draft BOOLEAN NOT NULL DEFAULT TRUE, status VARCHAR(50) NOT NULL DEFAULT 'DRAFT', -- DRAFT, PENDING_APPROVAL, APPROVED, PUBLISHED, ARCHIVED current_version_id UUID, tags TEXT[] DEFAULT '{}', created_by UUID NOT NULL REFERENCES users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(tenant_id, rule_code) ); CREATE TABLE rule_versions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), rule_id UUID NOT NULL REFERENCES rules(id) ON DELETE CASCADE, tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, version_number INTEGER NOT NULL, version_label VARCHAR(50), -- e.g., v1.0, v1.1 rule_definition JSONB NOT NULL, -- Complete rule AST change_summary TEXT, is_current BOOLEAN NOT NULL DEFAULT FALSE, status VARCHAR(50) NOT NULL DEFAULT 'DRAFT', approved_by UUID REFERENCES users(id), approved_at TIMESTAMPTZ, published_by UUID REFERENCES users(id), published_at TIMESTAMPTZ, created_by UUID NOT NULL REFERENCES users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(rule_id, version_number) ); -- Rule Definition JSON Structure (stored in rule_versions.rule_definition): -- { -- "conditions": { -- "operator": "AND|OR", -- "rules": [ -- { -- "id": "uuid", -- "field": "bureau_score", -- "operator": "LESS_THAN", -- "value": 650, -- "valueType": "NUMBER" -- }, -- { -- "operator": "OR", -- "rules": [...] -- nested group -- } -- ] -- }, -- "actions": [ -- { "type": "SET_DECISION", "value": "REJECT" }, -- { "type": "SET_RISK", "value": "HIGH" }, -- { "type": "ADD_DEVIATION", "value": "LOW_BUREAU_SCORE" }, -- { "type": "SET_FIELD", "field": "recommendation", "value": "Reject" } -- ], -- "metadata": { -- "executionOrder": 1, -- "stopOnMatch": true, -- "errorHandling": "SKIP" -- } -- } CREATE TABLE rule_scopes ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), rule_id UUID NOT NULL REFERENCES rules(id) ON DELETE CASCADE, tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, scope_type VARCHAR(50) NOT NULL, -- PRODUCT, BRANCH, STAGE, USER_ROLE, GLOBAL scope_value VARCHAR(200) NOT NULL, -- product_code, branch_code, stage_code, role_code, or '*' for all is_excluded BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE rule_sets ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, set_code VARCHAR(200) NOT NULL, set_name VARCHAR(500) NOT NULL, description TEXT, execution_mode VARCHAR(50) NOT NULL DEFAULT 'ALL', -- ALL, FIRST_MATCH, SCORED is_active BOOLEAN NOT NULL DEFAULT TRUE, created_by UUID NOT NULL REFERENCES users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(tenant_id, set_code) ); CREATE TABLE rule_set_members ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), set_id UUID NOT NULL REFERENCES rule_sets(id) ON DELETE CASCADE, rule_id UUID NOT NULL REFERENCES rules(id) ON DELETE CASCADE, sort_order INTEGER NOT NULL DEFAULT 0, weight DECIMAL(5,2) DEFAULT 1.0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(set_id, rule_id) ); -- ============================================================ -- SCHEMA: FIELD CATALOG (Dynamic Field Mapping) -- ============================================================ CREATE TABLE field_catalog ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE, -- NULL = global field_path VARCHAR(500) NOT NULL, -- e.g., "bureau.cibil_score", "applicant.age" display_name VARCHAR(200) NOT NULL, description TEXT, data_type VARCHAR(50) NOT NULL, -- NUMBER, STRING, BOOLEAN, DATE, ARRAY, OBJECT category VARCHAR(100), -- BUREAU, INCOME, PERSONAL, VEHICLE, etc. sample_values JSONB, validation_rules JSONB, is_system_field BOOLEAN NOT NULL DEFAULT FALSE, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(tenant_id, field_path) ); CREATE TABLE custom_functions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE, function_name VARCHAR(200) NOT NULL, display_name VARCHAR(200) NOT NULL, description TEXT, category VARCHAR(100), return_type VARCHAR(50) NOT NULL, parameters JSONB NOT NULL DEFAULT '[]'::JSONB, implementation TEXT NOT NULL, -- C# expression or script is_system_fn BOOLEAN NOT NULL DEFAULT FALSE, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(tenant_id, function_name) ); -- ============================================================ -- SCHEMA: EXECUTION ENGINE -- ============================================================ CREATE TABLE execution_requests ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id), correlation_id VARCHAR(200) UNIQUE, application_id VARCHAR(200), product_code VARCHAR(100), branch_code VARCHAR(100), stage_code VARCHAR(100), rule_set_id UUID REFERENCES rule_sets(id), input_payload JSONB NOT NULL, input_hash VARCHAR(64), status VARCHAR(50) NOT NULL DEFAULT 'PENDING', -- PENDING, PROCESSING, COMPLETED, FAILED priority INTEGER NOT NULL DEFAULT 5, source_system VARCHAR(200), api_key_id UUID REFERENCES api_keys(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), processing_started_at TIMESTAMPTZ, completed_at TIMESTAMPTZ, processing_ms INTEGER ); CREATE TABLE execution_results ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), request_id UUID NOT NULL REFERENCES execution_requests(id) ON DELETE CASCADE, tenant_id UUID NOT NULL REFERENCES tenants(id), final_decision VARCHAR(50) NOT NULL, -- APPROVE, REJECT, DEVIATION, REFER, PENDING risk_score DECIMAL(5,2), risk_category VARCHAR(50), -- LOW, MEDIUM, HIGH, CRITICAL traffic_light VARCHAR(10), -- GREEN, AMBER, RED total_rules_evaluated INTEGER NOT NULL DEFAULT 0, rules_passed INTEGER NOT NULL DEFAULT 0, rules_failed INTEGER NOT NULL DEFAULT 0, rules_skipped INTEGER NOT NULL DEFAULT 0, deviations_count INTEGER NOT NULL DEFAULT 0, execution_ms INTEGER, rule_results JSONB NOT NULL DEFAULT '[]'::JSONB, field_values JSONB NOT NULL DEFAULT '{}'::JSONB, ai_summary TEXT, ai_analysis JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE rule_execution_details ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), result_id UUID NOT NULL REFERENCES execution_results(id) ON DELETE CASCADE, rule_id UUID NOT NULL REFERENCES rules(id), rule_code VARCHAR(200) NOT NULL, rule_name VARCHAR(500) NOT NULL, version_number INTEGER NOT NULL, execution_order INTEGER NOT NULL, is_matched BOOLEAN NOT NULL, conditions_evaluated JSONB NOT NULL DEFAULT '[]'::JSONB, actions_executed JSONB NOT NULL DEFAULT '[]'::JSONB, execution_ms INTEGER, error_message TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- ============================================================ -- SCHEMA: DEVIATIONS -- ============================================================ CREATE TABLE deviation_types ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE, deviation_code VARCHAR(200) NOT NULL, deviation_name VARCHAR(500) NOT NULL, category VARCHAR(100), default_severity VARCHAR(50) NOT NULL DEFAULT 'MEDIUM', -- LOW, MEDIUM, HIGH, CRITICAL description TEXT, recommended_action TEXT, requires_approval BOOLEAN NOT NULL DEFAULT FALSE, approver_role VARCHAR(200), is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(tenant_id, deviation_code) ); CREATE TABLE execution_deviations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), result_id UUID NOT NULL REFERENCES execution_results(id) ON DELETE CASCADE, rule_id UUID REFERENCES rules(id), deviation_type_id UUID REFERENCES deviation_types(id), deviation_code VARCHAR(200) NOT NULL, deviation_name VARCHAR(500) NOT NULL, severity VARCHAR(50) NOT NULL, reason TEXT NOT NULL, field_path VARCHAR(500), actual_value TEXT, expected_value TEXT, recommended_action TEXT, is_overridden BOOLEAN NOT NULL DEFAULT FALSE, overridden_by UUID REFERENCES users(id), override_reason TEXT, override_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- ============================================================ -- SCHEMA: DECISION REPORTS -- ============================================================ CREATE TABLE decision_reports ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), request_id UUID NOT NULL UNIQUE REFERENCES execution_requests(id), tenant_id UUID NOT NULL REFERENCES tenants(id), report_number VARCHAR(100) UNIQUE, application_id VARCHAR(200), product_code VARCHAR(100), final_decision VARCHAR(50) NOT NULL, risk_score DECIMAL(5,2), risk_category VARCHAR(50), traffic_light VARCHAR(10), summary TEXT, strengths TEXT[], weaknesses TEXT[], deviations_summary TEXT, approval_recommendation TEXT, rejection_reasons TEXT[], additional_docs TEXT[], underwriting_notes TEXT, report_json JSONB, pdf_storage_path TEXT, excel_storage_path TEXT, generated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), expires_at TIMESTAMPTZ ); -- ============================================================ -- SCHEMA: SANDBOX & TESTING -- ============================================================ CREATE TABLE sandbox_sessions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, session_name VARCHAR(200) NOT NULL, rule_set_id UUID REFERENCES rule_sets(id), test_payload JSONB NOT NULL, result JSONB, status VARCHAR(50) NOT NULL DEFAULT 'PENDING', created_by UUID NOT NULL REFERENCES users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), executed_at TIMESTAMPTZ ); -- ============================================================ -- SCHEMA: AI ENGINE -- ============================================================ CREATE TABLE ai_prompts ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE, prompt_code VARCHAR(200) NOT NULL, prompt_name VARCHAR(300) NOT NULL, prompt_template TEXT NOT NULL, model VARCHAR(100) DEFAULT 'gpt-4o', temperature DECIMAL(3,2) DEFAULT 0.3, max_tokens INTEGER DEFAULT 2000, is_system BOOLEAN NOT NULL DEFAULT FALSE, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(tenant_id, prompt_code) ); CREATE TABLE ai_generated_rules ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, user_prompt TEXT NOT NULL, generated_rule JSONB NOT NULL, rule_id UUID REFERENCES rules(id), model_used VARCHAR(100), tokens_used INTEGER, is_accepted BOOLEAN, feedback TEXT, created_by UUID NOT NULL REFERENCES users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- ============================================================ -- SCHEMA: AUDIT & COMPLIANCE -- ============================================================ CREATE TABLE audit_logs ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id), user_id UUID REFERENCES users(id), action VARCHAR(200) NOT NULL, entity_type VARCHAR(100) NOT NULL, entity_id VARCHAR(200), old_values JSONB, new_values JSONB, ip_address INET, user_agent TEXT, request_id VARCHAR(200), is_success BOOLEAN NOT NULL DEFAULT TRUE, error_message TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE rule_approvals ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), rule_id UUID NOT NULL REFERENCES rules(id) ON DELETE CASCADE, version_id UUID NOT NULL REFERENCES rule_versions(id) ON DELETE CASCADE, tenant_id UUID NOT NULL REFERENCES tenants(id), requested_by UUID NOT NULL REFERENCES users(id), requested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), reviewed_by UUID REFERENCES users(id), reviewed_at TIMESTAMPTZ, status VARCHAR(50) NOT NULL DEFAULT 'PENDING', -- PENDING, APPROVED, REJECTED comments TEXT, notification_sent BOOLEAN NOT NULL DEFAULT FALSE ); -- ============================================================ -- SCHEMA: MARKETPLACE -- ============================================================ CREATE TABLE marketplace_rules ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), rule_code VARCHAR(200) UNIQUE NOT NULL, rule_name VARCHAR(500) NOT NULL, description TEXT, category VARCHAR(100), product_types TEXT[], rule_definition JSONB NOT NULL, author VARCHAR(200), version VARCHAR(50), downloads INTEGER NOT NULL DEFAULT 0, rating DECIMAL(3,2) DEFAULT 0, tags TEXT[], is_featured BOOLEAN NOT NULL DEFAULT FALSE, is_active BOOLEAN NOT NULL DEFAULT TRUE, published_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE marketplace_imports ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL REFERENCES tenants(id), marketplace_rule_id UUID NOT NULL REFERENCES marketplace_rules(id), imported_rule_id UUID REFERENCES rules(id), imported_by UUID NOT NULL REFERENCES users(id), imported_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- ============================================================ -- INDEXES FOR PERFORMANCE -- ============================================================ -- Tenant isolation indexes CREATE INDEX idx_rules_tenant ON rules(tenant_id) WHERE is_active = TRUE; CREATE INDEX idx_rules_tenant_category ON rules(tenant_id, category_id) WHERE is_active = TRUE; CREATE INDEX idx_rules_tenant_type ON rules(tenant_id, rule_type) WHERE is_active = TRUE AND is_published = TRUE; CREATE INDEX idx_rule_scopes_rule ON rule_scopes(rule_id); CREATE INDEX idx_rule_scopes_tenant_type ON rule_scopes(tenant_id, scope_type, scope_value); -- Execution performance indexes CREATE INDEX idx_exec_requests_tenant_status ON execution_requests(tenant_id, status, created_at DESC); CREATE INDEX idx_exec_requests_correlation ON execution_requests(correlation_id); CREATE INDEX idx_exec_results_request ON execution_results(request_id); CREATE INDEX idx_exec_results_tenant_decision ON execution_results(tenant_id, final_decision, created_at DESC); CREATE INDEX idx_rule_exec_details_result ON rule_execution_details(result_id); -- Audit indexes CREATE INDEX idx_audit_tenant_entity ON audit_logs(tenant_id, entity_type, entity_id, created_at DESC); CREATE INDEX idx_audit_tenant_user ON audit_logs(tenant_id, user_id, created_at DESC); -- User lookup CREATE INDEX idx_users_tenant_email ON users(tenant_id, email) WHERE is_active = TRUE; CREATE INDEX idx_users_tenant_username ON users(tenant_id, username) WHERE is_active = TRUE; -- Field catalog full-text CREATE INDEX idx_field_catalog_path ON field_catalog USING GIN(to_tsvector('english', field_path || ' ' || display_name)); -- Execution date range queries CREATE INDEX idx_exec_requests_tenant_date ON execution_requests(tenant_id, created_at DESC); CREATE INDEX idx_decision_reports_tenant ON decision_reports(tenant_id, generated_at DESC); -- ============================================================ -- SEED DATA: SYSTEM PERMISSIONS -- ============================================================ INSERT INTO permissions (permission_code, permission_name, module, description) VALUES -- Rule Management ('RULE.VIEW', 'View Rules', 'RULE_ENGINE', 'View all rules'), ('RULE.CREATE', 'Create Rules', 'RULE_ENGINE', 'Create new rules'), ('RULE.EDIT', 'Edit Rules', 'RULE_ENGINE', 'Edit existing rules'), ('RULE.DELETE', 'Delete Rules', 'RULE_ENGINE', 'Delete rules'), ('RULE.PUBLISH', 'Publish Rules', 'RULE_ENGINE', 'Publish rules to production'), ('RULE.APPROVE', 'Approve Rules', 'RULE_ENGINE', 'Approve rule changes'), ('RULE.CLONE', 'Clone Rules', 'RULE_ENGINE', 'Clone existing rules'), ('RULE.VERSION', 'Manage Rule Versions', 'RULE_ENGINE', 'View and manage rule versions'), -- Execution ('EXECUTION.VIEW', 'View Executions', 'EXECUTION', 'View execution results'), ('EXECUTION.EXECUTE', 'Execute Rules', 'EXECUTION', 'Execute rule engine via API'), ('EXECUTION.SANDBOX', 'Use Sandbox', 'EXECUTION', 'Use sandbox testing environment'), -- Client Management ('CLIENT.VIEW', 'View Clients', 'CLIENT_MGMT', 'View client list'), ('CLIENT.CREATE', 'Create Clients', 'CLIENT_MGMT', 'Create new clients'), ('CLIENT.EDIT', 'Edit Clients', 'CLIENT_MGMT', 'Edit client configuration'), ('CLIENT.MANAGE_USERS', 'Manage Client Users', 'CLIENT_MGMT', 'Manage users within tenant'), -- Reports ('REPORT.VIEW', 'View Reports', 'REPORTS', 'View decision reports'), ('REPORT.EXPORT', 'Export Reports', 'REPORTS', 'Export reports to PDF/Excel'), -- AI Engine ('AI.GENERATE', 'AI Rule Generation', 'AI_ENGINE', 'Use AI to generate rules'), ('AI.ANALYSIS', 'AI Credit Analysis', 'AI_ENGINE', 'Access AI credit analysis'), -- Admin ('ADMIN.FULL', 'Full Admin Access', 'ADMIN', 'Complete administrative access'), ('AUDIT.VIEW', 'View Audit Logs', 'AUDIT', 'Access audit trail'); -- ============================================================ -- SEED DATA: GLOBAL FIELD CATALOG -- ============================================================ INSERT INTO field_catalog (field_path, display_name, data_type, category, is_system_field) VALUES -- Personal ('applicant.age', 'Applicant Age', 'NUMBER', 'PERSONAL', TRUE), ('applicant.date_of_birth', 'Date of Birth', 'DATE', 'PERSONAL', TRUE), ('applicant.gender', 'Gender', 'STRING', 'PERSONAL', TRUE), ('applicant.pan_number', 'PAN Number', 'STRING', 'KYC', TRUE), ('applicant.aadhaar_number', 'Aadhaar Number', 'STRING', 'KYC', TRUE), ('applicant.mobile', 'Mobile Number', 'STRING', 'PERSONAL', TRUE), ('applicant.email', 'Email Address', 'STRING', 'PERSONAL', TRUE), ('applicant.marital_status', 'Marital Status', 'STRING', 'PERSONAL', TRUE), ('applicant.caste_category', 'Caste Category', 'STRING', 'PERSONAL', TRUE), -- Employment ('employment.type', 'Employment Type', 'STRING', 'EMPLOYMENT', TRUE), ('employment.employer_name', 'Employer Name', 'STRING', 'EMPLOYMENT', TRUE), ('employment.monthly_income', 'Monthly Income', 'NUMBER', 'INCOME', TRUE), ('employment.annual_income', 'Annual Income', 'NUMBER', 'INCOME', TRUE), ('employment.vintage_months', 'Employment Vintage (Months)', 'NUMBER', 'EMPLOYMENT', TRUE), -- Bureau ('bureau.cibil_score', 'CIBIL Score', 'NUMBER', 'BUREAU', TRUE), ('bureau.experian_score', 'Experian Score', 'NUMBER', 'BUREAU', TRUE), ('bureau.equifax_score', 'Equifax Score', 'NUMBER', 'BUREAU', TRUE), ('bureau.crif_score', 'CRIF Score', 'NUMBER', 'BUREAU', TRUE), ('bureau.max_dpd_24m', 'Max DPD (24 Months)', 'NUMBER', 'BUREAU', TRUE), ('bureau.max_dpd_12m', 'Max DPD (12 Months)', 'NUMBER', 'BUREAU', TRUE), ('bureau.total_active_loans', 'Total Active Loans', 'NUMBER', 'BUREAU', TRUE), ('bureau.unsecured_outstanding', 'Unsecured Outstanding Amount', 'NUMBER', 'BUREAU', TRUE), ('bureau.secured_outstanding', 'Secured Outstanding Amount', 'NUMBER', 'BUREAU', TRUE), ('bureau.total_emi_obligation', 'Total EMI Obligation', 'NUMBER', 'BUREAU', TRUE), ('bureau.written_off_amount', 'Written Off Amount', 'NUMBER', 'BUREAU', TRUE), ('bureau.suit_filed', 'Suit Filed', 'BOOLEAN', 'BUREAU', TRUE), ('bureau.wilful_defaulter', 'Wilful Defaulter', 'BOOLEAN', 'BUREAU', TRUE), -- Income Ratios ('ratios.foir', 'FOIR (Fixed Obligation to Income Ratio)', 'NUMBER', 'RATIOS', TRUE), ('ratios.ltv', 'LTV (Loan to Value Ratio)', 'NUMBER', 'RATIOS', TRUE), ('ratios.dscr', 'DSCR (Debt Service Coverage Ratio)', 'NUMBER', 'RATIOS', TRUE), -- Loan ('loan.amount', 'Loan Amount', 'NUMBER', 'LOAN', TRUE), ('loan.tenure_months', 'Loan Tenure (Months)', 'NUMBER', 'LOAN', TRUE), ('loan.emi', 'EMI Amount', 'NUMBER', 'LOAN', TRUE), ('loan.interest_rate', 'Interest Rate', 'NUMBER', 'LOAN', TRUE), ('loan.purpose', 'Loan Purpose', 'STRING', 'LOAN', TRUE), -- Vehicle ('vehicle.type', 'Vehicle Type', 'STRING', 'VEHICLE', TRUE), ('vehicle.make', 'Vehicle Make', 'STRING', 'VEHICLE', TRUE), ('vehicle.model', 'Vehicle Model', 'STRING', 'VEHICLE', TRUE), ('vehicle.year', 'Vehicle Year', 'NUMBER', 'VEHICLE', TRUE), ('vehicle.age_years', 'Vehicle Age (Years)', 'NUMBER', 'VEHICLE', TRUE), ('vehicle.valuation', 'Vehicle Valuation', 'NUMBER', 'VEHICLE', TRUE), ('vehicle.registration_number', 'Registration Number', 'STRING', 'VEHICLE', TRUE), -- FI ('fi.verified', 'FI Verified', 'BOOLEAN', 'FI', TRUE), ('fi.residence_type', 'Residence Type', 'STRING', 'FI', TRUE), ('fi.address_match', 'Address Match', 'BOOLEAN', 'FI', TRUE), ('fi.mobile_match', 'Mobile Match', 'BOOLEAN', 'FI', TRUE), ('fi.negative', 'FI Negative', 'BOOLEAN', 'FI', TRUE), -- GST & ITR ('gst.turnover', 'GST Turnover', 'NUMBER', 'GST', TRUE), ('gst.filing_months', 'GST Filing Months', 'NUMBER', 'GST', TRUE), ('itr.gross_income', 'ITR Gross Income', 'NUMBER', 'ITR', TRUE), ('itr.net_income', 'ITR Net Income', 'NUMBER', 'ITR', TRUE), ('itr.years_filed', 'ITR Years Filed', 'NUMBER', 'ITR', TRUE), -- Fraud ('fraud.score', 'Fraud Score', 'NUMBER', 'FRAUD', TRUE), ('fraud.blacklisted', 'Blacklisted', 'BOOLEAN', 'FRAUD', TRUE), ('fraud.device_score', 'Device Risk Score', 'NUMBER', 'FRAUD', TRUE); -- ============================================================ -- SEED DATA: DEVIATION TYPES -- ============================================================ INSERT INTO deviation_types (deviation_code, deviation_name, category, default_severity, description, recommended_action) VALUES ('LOW_BUREAU_SCORE', 'Low Bureau Score', 'BUREAU', 'HIGH', 'Bureau/CIBIL score below threshold', 'Obtain credit counseling letter or additional collateral'), ('HIGH_DPD', 'High DPD', 'BUREAU', 'HIGH', 'Days Past Due exceeds acceptable limit', 'Explain previous defaults with supporting docs'), ('HIGH_FOIR', 'High FOIR', 'INCOME', 'MEDIUM', 'Fixed Obligation to Income Ratio exceeds limit', 'Consider reducing loan amount or tenure'), ('INCOME_MISMATCH', 'Income Mismatch', 'INCOME', 'MEDIUM', 'Stated income does not match verified income', 'Provide additional income proof documents'), ('NEGATIVE_FI', 'Negative FI Report', 'FI', 'HIGH', 'Field Investigation report is negative', 'Second FI verification required'), ('ADDRESS_MISMATCH', 'Address Mismatch', 'FI', 'MEDIUM', 'Applicant address does not match records', 'Obtain additional address proof'), ('MOBILE_MISMATCH', 'Mobile Number Mismatch', 'FI', 'LOW', 'Mobile number not matching bureau records', 'Verify mobile ownership'), ('HIGH_LTV', 'High LTV', 'VALUATION', 'MEDIUM', 'Loan to Value ratio exceeds limit', 'Additional down payment required'), ('VEHICLE_AGE', 'Vehicle Age Deviation', 'VEHICLE', 'MEDIUM', 'Vehicle age exceeds permissible limit', 'Obtain extended warranty or reduce tenure'), ('MULTIPLE_LOANS', 'Multiple Active Loans', 'BUREAU', 'MEDIUM', 'High number of active loans', 'Review repayment capacity'), ('WRITTEN_OFF', 'Written Off Account', 'BUREAU', 'CRITICAL', 'Previous written off account found', 'NOC from previous lender required'), ('SUIT_FILED', 'Suit Filed', 'BUREAU', 'CRITICAL', 'Legal suit filed against applicant', 'Legal verification required'), ('AGE_DEVIATION', 'Age Deviation', 'PERSONAL', 'MEDIUM', 'Applicant age outside standard limits', 'Additional life insurance required'), ('LOW_VINTAGE', 'Low Employment Vintage', 'EMPLOYMENT', 'LOW', 'Employment tenure below minimum threshold', 'Previous employment proof required'), ('LOW_GST_FILING', 'Low GST Filing Compliance', 'GST', 'MEDIUM', 'GST filing not consistent', 'Last 12 months GST returns required'), ('FRAUD_RISK', 'Fraud Risk Detected', 'FRAUD', 'CRITICAL', 'High fraud risk score detected', 'Enhanced due diligence required'); -- ============================================================ -- TRIGGERS: AUTO UPDATE TIMESTAMPS -- ============================================================ CREATE OR REPLACE FUNCTION update_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_tenants_updated_at BEFORE UPDATE ON tenants FOR EACH ROW EXECUTE FUNCTION update_updated_at(); CREATE TRIGGER trg_users_updated_at BEFORE UPDATE ON users FOR EACH ROW EXECUTE FUNCTION update_updated_at(); CREATE TRIGGER trg_roles_updated_at BEFORE UPDATE ON roles FOR EACH ROW EXECUTE FUNCTION update_updated_at(); CREATE TRIGGER trg_rules_updated_at BEFORE UPDATE ON rules FOR EACH ROW EXECUTE FUNCTION update_updated_at(); CREATE TRIGGER trg_rule_sets_updated_at BEFORE UPDATE ON rule_sets FOR EACH ROW EXECUTE FUNCTION update_updated_at(); -- Auto-generate report number CREATE OR REPLACE FUNCTION generate_report_number() RETURNS TRIGGER AS $$ BEGIN NEW.report_number = 'BRE-' || TO_CHAR(NOW(), 'YYYYMMDD') || '-' || LPAD(nextval('report_seq')::TEXT, 6, '0'); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE SEQUENCE IF NOT EXISTS report_seq START 1; CREATE TRIGGER trg_decision_report_number BEFORE INSERT ON decision_reports FOR EACH ROW EXECUTE FUNCTION generate_report_number(); -- ============================================================ -- VIEWS FOR ANALYTICS -- ============================================================ CREATE OR REPLACE VIEW v_execution_summary AS SELECT er.tenant_id, DATE_TRUNC('day', eq.created_at) AS execution_date, COUNT(*) AS total_executions, SUM(CASE WHEN er.final_decision = 'APPROVE' THEN 1 ELSE 0 END) AS approved, SUM(CASE WHEN er.final_decision = 'REJECT' THEN 1 ELSE 0 END) AS rejected, SUM(CASE WHEN er.final_decision = 'DEVIATION' THEN 1 ELSE 0 END) AS deviations, ROUND(AVG(er.risk_score), 2) AS avg_risk_score, ROUND(AVG(eq.processing_ms), 0) AS avg_processing_ms, SUM(CASE WHEN er.final_decision = 'APPROVE' THEN 1 ELSE 0 END) * 100.0 / COUNT(*) AS approval_rate FROM execution_results er JOIN execution_requests eq ON eq.id = er.request_id GROUP BY er.tenant_id, DATE_TRUNC('day', eq.created_at); CREATE OR REPLACE VIEW v_rule_hit_analysis AS SELECT red.rule_id, r.rule_code, r.rule_name, r.tenant_id, COUNT(*) AS total_evaluations, SUM(CASE WHEN red.is_matched THEN 1 ELSE 0 END) AS matched_count, ROUND(SUM(CASE WHEN red.is_matched THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) AS hit_rate, ROUND(AVG(red.execution_ms), 0) AS avg_execution_ms FROM rule_execution_details red JOIN rules r ON r.id = red.rule_id GROUP BY red.rule_id, r.rule_code, r.rule_name, r.tenant_id; CREATE OR REPLACE VIEW v_deviation_analysis AS SELECT ed.tenant_id, ed.deviation_code, ed.deviation_name, ed.severity, COUNT(*) AS occurrence_count, SUM(CASE WHEN ed.is_overridden THEN 1 ELSE 0 END) AS override_count, DATE_TRUNC('month', ed.created_at) AS month FROM execution_deviations ed GROUP BY ed.tenant_id, ed.deviation_code, ed.deviation_name, ed.severity, DATE_TRUNC('month', ed.created_at);