"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,code VARCHAR(30) NOT NULL,name VARCHAR(120) NOT NULL,account_type ENUM('cash','bank','petty_cash') NOT NULL,gl_account_id INT NOT NULL,bank_name VARCHAR(120) NULL,bank_account_no VARCHAR(80) NULL,bank_account_name VARCHAR(120) NULL,currency CHAR(3) NOT NULL DEFAULT 'IDR',opening_balance DECIMAL(18,2) NOT NULL DEFAULT 0,opening_date DATE NULL,is_active TINYINT(1) NOT NULL DEFAULT 1,created_at DATETIME NOT NULL,UNIQUE KEY uq_cash_account_code(code),UNIQUE KEY uq_cash_account_gl(gl_account_id)", 'cash_transactions'=>"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,transaction_no VARCHAR(60) NOT NULL,transaction_date DATE NOT NULL,cash_account_id BIGINT UNSIGNED NOT NULL,transaction_type ENUM('opening','receipt','payment','transfer_in','transfer_out','petty_cash','cash_advance','advance_settlement','reimbursement','bank_charge','interest','adjustment') NOT NULL,direction ENUM('in','out') NOT NULL,amount DECIMAL(18,2) NOT NULL,counter_account_id INT NULL,party_type VARCHAR(30) NULL,party_id BIGINT UNSIGNED NULL,reference_no VARCHAR(120) NULL,description TEXT NOT NULL,status ENUM('draft','submitted','approved','posted','rejected','reversed') NOT NULL DEFAULT 'draft',requires_approval TINYINT(1) NOT NULL DEFAULT 0,source_type VARCHAR(50) NULL,source_id BIGINT UNSIGNED NULL,journal_id INT NULL,idempotency_key VARCHAR(120) NOT NULL,created_by INT NULL,approved_by INT NULL,approved_at DATETIME NULL,posted_by INT NULL,posted_at DATETIME NULL,created_at DATETIME NOT NULL,UNIQUE KEY uq_cash_transaction_no(transaction_no),UNIQUE KEY uq_cash_transaction_idempotency(idempotency_key),KEY idx_cash_ledger(cash_account_id,transaction_date,status)", 'cash_transfers'=>"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,transfer_no VARCHAR(60) NOT NULL,transfer_date DATE NOT NULL,from_cash_account_id BIGINT UNSIGNED NOT NULL,to_cash_account_id BIGINT UNSIGNED NOT NULL,amount DECIMAL(18,2) NOT NULL,bank_charge DECIMAL(18,2) NOT NULL DEFAULT 0,reference_no VARCHAR(120) NULL,status ENUM('draft','submitted','approved','posted','rejected','reversed') NOT NULL DEFAULT 'draft',out_transaction_id BIGINT UNSIGNED NULL,in_transaction_id BIGINT UNSIGNED NULL,journal_id INT NULL,idempotency_key VARCHAR(120) NOT NULL,created_by INT NULL,approved_by INT NULL,UNIQUE KEY uq_cash_transfer_no(transfer_no),UNIQUE KEY uq_cash_transfer_key(idempotency_key)", 'bank_statement_imports'=>"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,cash_account_id BIGINT UNSIGNED NOT NULL,file_name VARCHAR(255) NOT NULL,statement_from DATE NULL,statement_to DATE NULL,opening_balance DECIMAL(18,2) NULL,closing_balance DECIMAL(18,2) NULL,file_hash CHAR(64) NOT NULL,status ENUM('imported','processing','completed','cancelled') NOT NULL DEFAULT 'imported',created_by INT NULL,created_at DATETIME NOT NULL,UNIQUE KEY uq_bank_import_hash(cash_account_id,file_hash)", 'bank_statement_lines'=>"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,import_id BIGINT UNSIGNED NOT NULL,line_no INT NOT NULL,transaction_date DATE NOT NULL,description TEXT NULL,reference_no VARCHAR(120) NULL,debit DECIMAL(18,2) NOT NULL DEFAULT 0,credit DECIMAL(18,2) NOT NULL DEFAULT 0,balance DECIMAL(18,2) NULL,match_status ENUM('unmatched','suggested','matched','ignored') NOT NULL DEFAULT 'unmatched',matched_cash_transaction_id BIGINT UNSIGNED NULL,match_score DECIMAL(5,2) NULL,UNIQUE KEY uq_bank_import_line(import_id,line_no)", 'bank_reconciliations'=>"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,reconciliation_no VARCHAR(60) NOT NULL,cash_account_id BIGINT UNSIGNED NOT NULL,period_end DATE NOT NULL,book_balance DECIMAL(18,2) NOT NULL,statement_balance DECIMAL(18,2) NOT NULL,outstanding_receipts DECIMAL(18,2) NOT NULL DEFAULT 0,outstanding_payments DECIMAL(18,2) NOT NULL DEFAULT 0,difference DECIMAL(18,2) NOT NULL,status ENUM('draft','balanced','variance','closed') NOT NULL DEFAULT 'draft',notes TEXT NULL,created_by INT NULL,closed_by INT NULL,closed_at DATETIME NULL,UNIQUE KEY uq_bank_reconciliation(reconciliation_no),UNIQUE KEY uq_bank_period(cash_account_id,period_end)", 'cash_forecasts'=>"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,forecast_date DATE NOT NULL,cash_account_id BIGINT UNSIGNED NULL,direction ENUM('in','out') NOT NULL,amount DECIMAL(18,2) NOT NULL,probability DECIMAL(5,2) NOT NULL DEFAULT 100,description VARCHAR(255) NOT NULL,source_type VARCHAR(50) NULL,source_id BIGINT UNSIGNED NULL,status ENUM('planned','realized','cancelled') NOT NULL DEFAULT 'planned',created_by INT NULL,created_at DATETIME NOT NULL" );foreach($tables as$t=>$s)$this->db->query("CREATE TABLE IF NOT EXISTS `$t`($s) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"); $this->db->query("INSERT IGNORE INTO cash_accounts(code,name,account_type,gl_account_id,created_at) SELECT CONCAT('GL-',kode_akun),nama_akun,IF(LOWER(nama_akun) LIKE '%bank%','bank',IF(LOWER(nama_akun) LIKE '%petty%','petty_cash','cash')),id,NOW() FROM accounts WHERE sub_tipe='kas'"); $admin=$this->db->where('nama_role','Admin')->get('roles')->row();if(!$this->db->where('code','CASH_PAYMENT_LARGE')->count_all_results('approval_workflows')){$this->db->insert('approval_workflows',array('code'=>'CASH_PAYMENT_LARGE','module'=>'cash_bank','entity_type'=>'cash_transaction','name'=>'Persetujuan Pembayaran Besar','require_separation'=>1,'is_active'=>1,'created_at'=>date('Y-m-d H:i:s')));if($admin)$this->db->insert('approval_workflow_steps',array('workflow_id'=>$this->db->insert_id(),'step_order'=>1,'name'=>'Persetujuan Admin','approver_role_id'=>$admin->id));} $this->db->query('DROP TRIGGER IF EXISTS trg_cash_posted_no_delete');$this->db->query("CREATE TRIGGER trg_cash_posted_no_delete BEFORE DELETE ON cash_transactions FOR EACH ROW BEGIN IF OLD.status IN('posted','reversed') THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Posted cash transaction cannot be deleted'; END IF; END"); } public function down(){throw new RuntimeException('Gunakan backup untuk rollback Tahap 9.');} }