-- MySQL-compatible Schema for Ghana School ERP Accounting Module -- Translated from user-provided PostgreSQL schema. CREATE TABLE IF NOT EXISTS `accounts` ( `id` CHAR(36) PRIMARY KEY, `account_code` VARCHAR(50) UNIQUE NOT NULL, `account_name` VARCHAR(255) NOT NULL, `account_type` VARCHAR(20) NOT NULL CHECK (`account_type` IN ('Asset', 'Liability', 'Equity', 'Revenue', 'ContraRevenue', 'Expense')), `normal_balance` VARCHAR(6) NOT NULL CHECK (`normal_balance` IN ('Debit', 'Credit')), `parent_account_id` CHAR(36), `is_active` BOOLEAN NOT NULL DEFAULT TRUE, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (`parent_account_id`) REFERENCES `accounts`(`id`) ON DELETE RESTRICT ); CREATE INDEX `idx_accounts_code` ON `accounts`(`account_code`); CREATE INDEX `idx_accounts_parent` ON `accounts`(`parent_account_id`); CREATE INDEX `idx_accounts_type` ON `accounts`(`account_type`); CREATE TABLE IF NOT EXISTS `accounting_periods` ( `id` CHAR(36) PRIMARY KEY, `name` VARCHAR(100) NOT NULL, `start_date` DATE NOT NULL, `end_date` DATE NOT NULL, `is_closed` BOOLEAN NOT NULL DEFAULT FALSE, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT `chk_period_dates` CHECK (`end_date` > `start_date`) ); CREATE INDEX `idx_periods_dates` ON `accounting_periods`(`start_date`, `end_date`); CREATE TABLE IF NOT EXISTS `journal_entries` ( `id` CHAR(36) PRIMARY KEY, `entry_number` VARCHAR(50) UNIQUE NOT NULL, `entry_date` DATE NOT NULL, `description` TEXT, `period_id` CHAR(36) NOT NULL, `source_module` VARCHAR(20) NOT NULL CHECK (`source_module` IN ('Manual', 'Fees', 'Payments', 'Payroll', 'Expenses')), `source_reference_id` CHAR(36), `status` VARCHAR(10) NOT NULL DEFAULT 'Draft' CHECK (`status` IN ('Draft', 'Posted', 'Reversed')), `reversed_by` CHAR(36), `reverses_journal_id` CHAR(36), `created_by` CHAR(36) NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `posted_at` TIMESTAMP NULL, FOREIGN KEY (`period_id`) REFERENCES `accounting_periods`(`id`) ON DELETE RESTRICT, FOREIGN KEY (`reversed_by`) REFERENCES `journal_entries`(`id`), FOREIGN KEY (`reverses_journal_id`) REFERENCES `journal_entries`(`id`) ); CREATE INDEX `idx_journal_period` ON `journal_entries`(`period_id`); CREATE INDEX `idx_journal_date` ON `journal_entries`(`entry_date`); CREATE INDEX `idx_journal_status` ON `journal_entries`(`status`); CREATE INDEX `idx_journal_source` ON `journal_entries`(`source_module`, `source_reference_id`); CREATE TABLE IF NOT EXISTS `journal_lines` ( `id` CHAR(36) PRIMARY KEY, `journal_entry_id` CHAR(36) NOT NULL, `account_id` CHAR(36) NOT NULL, `debit_amount` DECIMAL(14,2) NOT NULL DEFAULT 0, `credit_amount` DECIMAL(14,2) NOT NULL DEFAULT 0, `line_description` TEXT, CONSTRAINT `chk_debit_or_credit` CHECK ((`debit_amount` > 0 AND `credit_amount` = 0) OR (`credit_amount` > 0 AND `debit_amount` = 0)), CHECK (`debit_amount` >= 0), CHECK (`credit_amount` >= 0), FOREIGN KEY (`journal_entry_id`) REFERENCES `journal_entries`(`id`) ON DELETE CASCADE, FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`) ON DELETE RESTRICT ); CREATE INDEX `idx_lines_journal` ON `journal_lines`(`journal_entry_id`); CREATE INDEX `idx_lines_account` ON `journal_lines`(`account_id`); CREATE TABLE IF NOT EXISTS `subledgers` ( `id` CHAR(36) PRIMARY KEY, `subledger_type` VARCHAR(20) NOT NULL CHECK (`subledger_type` IN ('Student', 'Vendor', 'Staff')), `reference_id` CHAR(36) NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT `unq_subledger` UNIQUE (`subledger_type`, `reference_id`) ); CREATE INDEX `idx_subledger_ref` ON `subledgers`(`subledger_type`, `reference_id`); CREATE TABLE IF NOT EXISTS `journal_line_subledgers` ( `id` CHAR(36) PRIMARY KEY, `journal_line_id` CHAR(36) NOT NULL, `subledger_id` CHAR(36) NOT NULL, CONSTRAINT `unq_line_subledger` UNIQUE (`journal_line_id`, `subledger_id`), FOREIGN KEY (`journal_line_id`) REFERENCES `journal_lines`(`id`) ON DELETE CASCADE, FOREIGN KEY (`subledger_id`) REFERENCES `subledgers`(`id`) ON DELETE RESTRICT ); CREATE INDEX `idx_jls_line` ON `journal_line_subledgers`(`journal_line_id`); CREATE INDEX `idx_jls_subledger` ON `journal_line_subledgers`(`subledger_id`); CREATE TABLE IF NOT EXISTS `audit_logs` ( `id` CHAR(36) PRIMARY KEY, `entity_type` VARCHAR(50) NOT NULL, `entity_id` CHAR(36) NOT NULL, `action` VARCHAR(20) NOT NULL CHECK (`action` IN ('CREATE', 'UPDATE', 'POST', 'REVERSE', 'DELETE')), `performed_by` CHAR(36) NOT NULL, `performed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `details` JSON ); CREATE INDEX `idx_audit_entity` ON `audit_logs`(`entity_type`, `entity_id`); CREATE INDEX `idx_audit_performed` ON `audit_logs`(`performed_at`);