Files
accounting_dev_v2/application/migrations/20260901000900_create_fixed_asset_subledger.php
T
2026-09-11 16:03:00 +07:00

25 lines
6.3 KiB
PHP

<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Migration_Create_fixed_asset_subledger extends CI_Migration
{
private function col($t,$c,$sql){if(!$this->db->field_exists($c,$t))$this->db->query("ALTER TABLE `$t` ADD `$c` $sql");}
public function up(){
foreach(array('category_id'=>'BIGINT UNSIGNED NULL','responsible_user_id'=>'INT NULL','source_type'=>"ENUM('purchase','warehouse','manual') NOT NULL DEFAULT 'manual'",'source_id'=>'BIGINT UNSIGNED NULL','capitalization_date'=>'DATE NULL','depreciation_method'=>"ENUM('straight_line','declining_balance') NOT NULL DEFAULT 'straight_line'",'asset_account_id'=>'INT NULL','accumulated_depreciation_account_id'=>'INT NULL','depreciation_expense_account_id'=>'INT NULL','impairment_total'=>'DECIMAL(18,2) NOT NULL DEFAULT 0','revaluation_total'=>'DECIMAL(18,2) NOT NULL DEFAULT 0','disposal_date'=>'DATE NULL','disposal_proceeds'=>'DECIMAL(18,2) NOT NULL DEFAULT 0','lifecycle_status'=>"ENUM('draft','active','fully_depreciated','disposed','lost','damaged') NOT NULL DEFAULT 'active'",'qr_code'=>'VARCHAR(120) NULL','version'=>'INT NOT NULL DEFAULT 1') as$c=>$s)$this->col('assets',$c,$s);
$this->db->query("UPDATE assets SET capitalization_date=COALESCE(capitalization_date,tanggal_perolehan),source_type=IF(sumber='gudang','warehouse','manual'),asset_account_id=COALESCE(asset_account_id,account_debit_id),qr_code=COALESCE(qr_code,CONCAT('ASSET:',kode_asset)),lifecycle_status=IF(status='aktif','active','disposed')");
$tables=array(
'asset_categories'=>"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,code VARCHAR(30) NOT NULL,name VARCHAR(120) NOT NULL,default_useful_life_months INT NOT NULL DEFAULT 48,default_residual_percent DECIMAL(7,4) NOT NULL DEFAULT 0,depreciation_method ENUM('straight_line','declining_balance') NOT NULL DEFAULT 'straight_line',asset_account_id INT NULL,accumulated_depreciation_account_id INT NULL,depreciation_expense_account_id INT NULL,is_active TINYINT(1) NOT NULL DEFAULT 1,UNIQUE KEY uq_asset_category_code(code)",
'asset_events'=>"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,asset_id INT NOT NULL,event_no VARCHAR(60) NOT NULL,event_type ENUM('acquisition','capitalization','addition','transfer','maintenance','depreciation','impairment','revaluation','disposal','lost','damaged','opname_adjustment','reversal') NOT NULL,event_date DATE NOT NULL,amount DECIMAL(18,2) NOT NULL DEFAULT 0,book_value_before DECIMAL(18,2) NULL,book_value_after DECIMAL(18,2) NULL,from_location_id INT NULL,to_location_id INT NULL,notes TEXT NULL,source_type VARCHAR(50) NULL,source_id BIGINT UNSIGNED NULL,journal_id INT NULL,idempotency_key VARCHAR(120) NOT NULL,created_by INT NULL,created_at DATETIME NOT NULL,UNIQUE KEY uq_asset_event_no(event_no),UNIQUE KEY uq_asset_event_idempotency(idempotency_key),KEY idx_asset_event(asset_id,event_date)",
'asset_depreciation_schedule'=>"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,asset_id INT NOT NULL,period CHAR(7) NOT NULL,due_date DATE NOT NULL,opening_book_value DECIMAL(18,2) NOT NULL,depreciation_amount DECIMAL(18,2) NOT NULL,closing_book_value DECIMAL(18,2) NOT NULL,status ENUM('scheduled','posted','skipped','reversed') NOT NULL DEFAULT 'scheduled',journal_id INT NULL,asset_event_id BIGINT UNSIGNED NULL,posted_at DATETIME NULL,UNIQUE KEY uq_asset_depreciation_period(asset_id,period)",
'asset_maintenance'=>"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,asset_id INT NOT NULL,maintenance_date DATE NOT NULL,vendor VARCHAR(150) NULL,description TEXT NOT NULL,cost DECIMAL(18,2) NOT NULL DEFAULT 0,next_due_date DATE NULL,attachment_id BIGINT UNSIGNED NULL,created_by INT NULL,created_at DATETIME NOT NULL",
'asset_opnames'=>"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,opname_no VARCHAR(60) NOT NULL,opname_date DATE NOT NULL,location_id INT NULL,status ENUM('draft','submitted','approved','posted','rejected') NOT NULL DEFAULT 'draft',notes TEXT NULL,created_by INT NULL,approved_by INT NULL,approved_at DATETIME NULL,UNIQUE KEY uq_asset_opname_no(opname_no)",
'asset_opname_lines'=>"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,asset_opname_id BIGINT UNSIGNED NOT NULL,asset_id INT NOT NULL,expected_status VARCHAR(30) NOT NULL,actual_status VARCHAR(30) NOT NULL,actual_location_id INT NULL,notes TEXT NULL,UNIQUE KEY uq_asset_opname_line(asset_opname_id,asset_id)",
'asset_reconciliations'=>"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,as_of_date DATE NOT NULL,subledger_cost DECIMAL(18,2) NOT NULL,gl_cost DECIMAL(18,2) NOT NULL,subledger_accumulated DECIMAL(18,2) NOT NULL,gl_accumulated DECIMAL(18,2) NOT NULL,difference DECIMAL(18,2) NOT NULL,status ENUM('matched','variance','resolved') NOT NULL,notes TEXT NULL,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 asset_events(asset_id,event_no,event_type,event_date,amount,book_value_before,book_value_after,notes,source_type,source_id,idempotency_key,created_at) SELECT id,CONCAT('LEGACY-AST-',id),'acquisition',tanggal_perolehan,nilai_perolehan,0,nilai_perolehan,'Backfill aset legacy tanpa jurnal baru','legacy_asset',id,CONCAT('LEGACY-ASSET-',id),COALESCE(created_at,NOW()) FROM assets");
$this->db->query("INSERT IGNORE INTO asset_depreciation_schedule(asset_id,period,due_date,opening_book_value,depreciation_amount,closing_book_value,status) SELECT asset_id,DATE_FORMAT(tanggal_dibuat,'%Y-%m'),tanggal_dibuat,GREATEST(0,a.nilai_perolehan-SUM(m.nilai) OVER(PARTITION BY m.asset_id ORDER BY m.tanggal_dibuat,m.id)+m.nilai),m.nilai,GREATEST(a.nilai_residu,a.nilai_perolehan-SUM(m.nilai) OVER(PARTITION BY m.asset_id ORDER BY m.tanggal_dibuat,m.id)),'posted' FROM asset_mutations m JOIN assets a ON a.id=m.asset_id WHERE m.tipe='penyusutan'");
$this->db->query('DROP TRIGGER IF EXISTS trg_asset_event_no_update');$this->db->query("CREATE TRIGGER trg_asset_event_no_update BEFORE UPDATE ON asset_events FOR EACH ROW SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Asset event is immutable'");
$this->db->query('DROP TRIGGER IF EXISTS trg_asset_event_no_delete');$this->db->query("CREATE TRIGGER trg_asset_event_no_delete BEFORE DELETE ON asset_events FOR EACH ROW SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='Asset event is immutable'");
}
public function down(){throw new RuntimeException('Gunakan backup untuk rollback Tahap 8.');}
}