SET NAMES utf8mb4; SET CHARACTER SET utf8mb4; -- ─── Tenants ────────────────────────────────────────────── CREATE TABLE tenants ( id CHAR(36) NOT NULL DEFAULT (UUID()), name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, phone VARCHAR(20) NULL, status ENUM('active','suspended','trial') NOT NULL DEFAULT 'trial', trial_ends_at DATETIME NULL, settings JSON DEFAULT (JSON_OBJECT()), created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted_at DATETIME NULL, PRIMARY KEY (id), UNIQUE KEY uq_tenants_email (email) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ─── Users ──────────────────────────────────────────────── CREATE TABLE users ( id CHAR(36) NOT NULL DEFAULT (UUID()), tenant_id CHAR(36) NOT NULL, name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, password_hash VARCHAR(255) NOT NULL, role ENUM('super_admin','admin','accountant','employee','viewer') NOT NULL, assigned_company_id CHAR(36) NULL, refresh_token_hash VARCHAR(255) NULL, totp_secret VARCHAR(64) NULL, totp_enabled TINYINT(1) NOT NULL DEFAULT 0, is_active TINYINT(1) NOT NULL DEFAULT 1, email_verified_at DATETIME NULL, last_login_at DATETIME NULL, last_login_ip VARCHAR(45) NULL, failed_login_count INT NOT NULL DEFAULT 0, locked_until DATETIME NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted_at DATETIME NULL, PRIMARY KEY (id), UNIQUE KEY uq_tenant_email (tenant_id, email), CONSTRAINT fk_users_tenant FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ─── API Keys ───────────────────────────────────────────── CREATE TABLE api_keys ( id CHAR(36) NOT NULL DEFAULT (UUID()), tenant_id CHAR(36) NOT NULL, user_id CHAR(36) NOT NULL, name VARCHAR(100) NOT NULL, public_key VARCHAR(64) NOT NULL, secret_hash VARCHAR(255) NOT NULL, permissions JSON DEFAULT (JSON_ARRAY('invoices:read','invoices:upload')), last_used_at DATETIME NULL, is_active TINYINT(1) NOT NULL DEFAULT 1, expires_at DATETIME NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uq_api_public_key (public_key), CONSTRAINT fk_apikeys_tenant FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE, CONSTRAINT fk_apikeys_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ─── Companies ──────────────────────────────────────────── CREATE TABLE companies ( id CHAR(36) NOT NULL DEFAULT (UUID()), tenant_id CHAR(36) NOT NULL, name VARCHAR(255) NOT NULL, name_en VARCHAR(255) NULL, tax_identification_number VARCHAR(20) NOT NULL, commercial_registration_number VARCHAR(50) NULL, address TEXT NULL, city VARCHAR(100) NULL, contact_email VARCHAR(255) NULL, contact_phone VARCHAR(20) NULL, jofotara_client_id_encrypted TEXT NULL, jofotara_secret_key_encrypted TEXT NULL, jofotara_income_source_sequence VARCHAR(50) NULL, certificate_path VARCHAR(255) NULL, certificate_password_encrypted TEXT NULL, is_jofotara_linked TINYINT(1) NOT NULL DEFAULT 0, is_active TINYINT(1) NOT NULL DEFAULT 1, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted_at DATETIME NULL, PRIMARY KEY (id), INDEX idx_companies_tenant (tenant_id), INDEX idx_companies_tin (tax_identification_number), CONSTRAINT fk_companies_tenant FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ─── Subscriptions ──────────────────────────────────────── CREATE TABLE subscriptions ( id CHAR(36) NOT NULL DEFAULT (UUID()), tenant_id CHAR(36) NOT NULL, plan ENUM('free','basic','office','pro','enterprise') NOT NULL DEFAULT 'basic', max_companies INT NOT NULL DEFAULT 3, max_invoices_per_month INT NOT NULL DEFAULT 50, max_users INT NOT NULL DEFAULT 2, price_jod DECIMAL(10,2) NOT NULL DEFAULT 0.00, invoices_used_this_month INT NOT NULL DEFAULT 0, status ENUM('active','past_due','cancelled','trial') NOT NULL DEFAULT 'active', current_period_start DATETIME NULL, current_period_end DATETIME NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uq_sub_tenant (tenant_id), CONSTRAINT fk_sub_tenant FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ─── Invoices ───────────────────────────────────────────── CREATE TABLE invoices ( id CHAR(36) NOT NULL DEFAULT (UUID()), tenant_id CHAR(36) NOT NULL, company_id CHAR(36) NOT NULL, uploaded_by CHAR(36) NULL, invoice_number VARCHAR(100) NULL, invoice_date DATE NULL, invoice_type ENUM('cash','credit') NOT NULL DEFAULT 'cash', ubl_type_code CHAR(3) NOT NULL DEFAULT '388', payment_method_code CHAR(3) NOT NULL DEFAULT '013', supplier_tin VARCHAR(20) NULL, supplier_name VARCHAR(255) NULL, supplier_address TEXT NULL, buyer_tin VARCHAR(20) NULL, buyer_national_id VARCHAR(20) NULL, buyer_name VARCHAR(255) NULL, subtotal DECIMAL(15,3) NOT NULL DEFAULT 0, discount_total DECIMAL(15,3) NOT NULL DEFAULT 0, tax_amount DECIMAL(15,3) NOT NULL DEFAULT 0, grand_total DECIMAL(15,3) NOT NULL DEFAULT 0, currency_code CHAR(3) NOT NULL DEFAULT 'JOD', status ENUM('uploaded','extracting','extracted','validated', 'validation_failed','submitting','approved','rejected') NOT NULL DEFAULT 'uploaded', original_file_path TEXT NULL, original_file_hash VARCHAR(64) NULL, invoice_category VARCHAR(20) NOT NULL DEFAULT 'simplified', validation_errors JSON NULL, qr_code TEXT NULL, jofotara_response JSON NULL, ai_provider VARCHAR(20) NULL, ai_confidence_score DECIMAL(4,3) NULL, ai_prompt_tokens INT NOT NULL DEFAULT 0, ai_completion_tokens INT NOT NULL DEFAULT 0, ai_total_cost DECIMAL(10,6) NOT NULL DEFAULT 0, ai_raw_response JSON NULL, idempotency_key VARCHAR(64) NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted_at DATETIME NULL, PRIMARY KEY (id), UNIQUE KEY uq_idempotency (idempotency_key), INDEX idx_invoices_tenant (tenant_id), INDEX idx_invoices_company (company_id), INDEX idx_invoices_status (status), INDEX idx_invoices_date (invoice_date), INDEX idx_invoices_file_hash (original_file_hash), CONSTRAINT fk_inv_tenant FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE, CONSTRAINT fk_inv_company FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE, CONSTRAINT fk_inv_user FOREIGN KEY (uploaded_by) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ─── Invoice Lines ──────────────────────────────────────── CREATE TABLE invoice_lines ( id CHAR(36) NOT NULL DEFAULT (UUID()), invoice_id CHAR(36) NOT NULL, line_number INT NOT NULL, description TEXT NOT NULL, quantity DECIMAL(15,3) NOT NULL, unit_price DECIMAL(15,3) NOT NULL, discount DECIMAL(15,3) NOT NULL DEFAULT 0, tax_rate DECIMAL(5,4) NOT NULL, tax_amount DECIMAL(15,3) NOT NULL DEFAULT 0, line_total DECIMAL(15,3) NOT NULL, PRIMARY KEY (id), INDEX idx_lines_invoice (invoice_id), CONSTRAINT fk_lines_invoice FOREIGN KEY (invoice_id) REFERENCES invoices(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ─── Audit Logs ─────────────────────────────────────────── CREATE TABLE audit_logs ( id CHAR(36) NOT NULL DEFAULT (UUID()), tenant_id CHAR(36) NULL, user_id CHAR(36) NULL, action VARCHAR(100) NOT NULL, entity_type VARCHAR(50) NULL, entity_id CHAR(36) NULL, old_data JSON NULL, new_data JSON NULL, ip_address VARCHAR(45) NULL, user_agent TEXT NULL, metadata JSON NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), INDEX idx_audit_tenant (tenant_id), INDEX idx_audit_action (action), INDEX idx_audit_created (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ─── Risk Scores ────────────────────────────────────────── CREATE TABLE risk_scores ( id CHAR(36) NOT NULL DEFAULT (UUID()), tenant_id CHAR(36) NOT NULL, company_id CHAR(36) NOT NULL, invoice_id CHAR(36) NULL, risk_type VARCHAR(50) NOT NULL, risk_level ENUM('low', 'medium', 'high', 'critical') NOT NULL DEFAULT 'low', score TINYINT UNSIGNED NOT NULL, reason TEXT NOT NULL, factors JSON NULL, is_resolved TINYINT(1) NOT NULL DEFAULT 0, resolved_by CHAR(36) NULL, resolved_at DATETIME NULL, calculated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), INDEX idx_risk_tenant (tenant_id), INDEX idx_risk_unresolved (is_resolved), CONSTRAINT fk_risk_tenant FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE, CONSTRAINT fk_risk_company FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE, CONSTRAINT fk_risk_invoice FOREIGN KEY (invoice_id) REFERENCES invoices(id) ON DELETE SET NULL, CONSTRAINT fk_risk_resolver FOREIGN KEY (resolved_by) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ─── Notifications ──────────────────────────────────────── CREATE TABLE notifications ( id CHAR(36) NOT NULL DEFAULT (UUID()), user_id CHAR(36) NOT NULL, title VARCHAR(255) NOT NULL, message TEXT NOT NULL, type VARCHAR(50) NOT NULL DEFAULT 'info', is_read TINYINT(1) NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), INDEX idx_notifications_user (user_id), CONSTRAINT fk_notifications_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ─── Queue Jobs (MySQL fallback when Redis unavailable) ─── CREATE TABLE queue_jobs ( id CHAR(36) NOT NULL DEFAULT (UUID()), type VARCHAR(100) NOT NULL, payload JSON NOT NULL, priority INT NOT NULL DEFAULT 0, attempts INT NOT NULL DEFAULT 0, max_attempts INT NOT NULL DEFAULT 3, status ENUM('pending','processing','completed','failed','dead') NOT NULL DEFAULT 'pending', error TEXT NULL, locked_at DATETIME NULL, locked_by VARCHAR(100) NULL, scheduled_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, completed_at DATETIME NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), INDEX idx_queue_pending (status, priority DESC, scheduled_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;