CI =& get_instance(); $this->CI->load->model('FiscalPeriodModel', 'fiscalPeriodModel'); $this->CI->load->library('TransactionService'); $this->CI->load->library('AuditService'); } public function periodForDate($date) { $parsed = DateTime::createFromFormat('Y-m-d', (string) $date); if (!$parsed || $parsed->format('Y-m-d') !== $date) { throw new BusinessException('Tanggal periode accounting tidak valid.'); } $period = $this->CI->fiscalPeriodModel->findByDate($date); if (!$period) { throw new BusinessException('Periode accounting untuk tanggal ' . $date . ' belum tersedia.'); } return $period; } public function assertOpen($date) { $period = $this->periodForDate($date); if ($period->status !== 'open') { throw new BusinessException('Periode ' . $period->name . ' berstatus ' . $period->status . ' dan tidak menerima transaksi.'); } return $period; } public function changeStatus($periodId, $targetStatus, $userId, $reason = '') { $allowed = array('open', 'soft_closed', 'closed'); if (!in_array($targetStatus, $allowed, true)) { throw new BusinessException('Status periode tidak valid.'); } return $this->CI->transactionservice->run(function () use ($periodId, $targetStatus, $userId, $reason) { $period = $this->CI->db->query('SELECT * FROM accounting_periods WHERE id = ? FOR UPDATE', array((int) $periodId))->row(); if (!$period) { throw new BusinessException('Periode accounting tidak ditemukan.', array(), 404); } if ($period->status === $targetStatus) { return $period; } $transitions = array( 'open' => array('soft_closed', 'closed'), 'soft_closed' => array('open', 'closed'), 'closed' => array('open') ); if (!in_array($targetStatus, $transitions[$period->status], true)) { throw new BusinessException('Perubahan status periode tidak diizinkan.'); } if ($targetStatus === 'open' && $period->status !== 'open' && trim($reason) === '') { throw new BusinessException('Alasan membuka kembali periode wajib diisi.'); } if (in_array($targetStatus, array('soft_closed', 'closed'), true)) { $pending = $this->CI->db ->where('tanggal >=', $period->start_date) ->where('tanggal <=', $period->end_date) ->where_in('status', array('draft', 'submitted', 'approved')) ->count_all_results('journals'); if ($pending > 0) { throw new BusinessException('Periode masih mempunyai ' . $pending . ' jurnal yang belum diposting atau ditolak.'); } } $update = array('status' => $targetStatus, 'updated_at' => date('Y-m-d H:i:s')); if ($targetStatus === 'closed') { $update['closed_at'] = date('Y-m-d H:i:s'); $update['closed_by'] = (int) $userId; } elseif ($targetStatus === 'open') { $update['reopened_at'] = date('Y-m-d H:i:s'); $update['reopened_by'] = (int) $userId; } $this->CI->db->where('id', $period->id)->update('accounting_periods', $update); $this->CI->db->insert('accounting_period_logs', array( 'accounting_period_id' => $period->id, 'from_status' => $period->status, 'to_status' => $targetStatus, 'reason' => trim($reason) ?: null, 'changed_by' => (int) $userId, 'changed_at' => date('Y-m-d H:i:s') )); $notClosed = $this->CI->db ->where('fiscal_year_id', $period->fiscal_year_id) ->where('status !=', 'closed') ->count_all_results('accounting_periods'); $this->CI->db->where('id', $period->fiscal_year_id)->update('fiscal_years', array( 'status' => $notClosed === 0 ? 'closed' : 'open' )); $after=$this->CI->fiscalPeriodModel->find($period->id); $this->CI->auditservice->record('accounting_period','accounting_period',$period->id,'change_status',$period,$after,$userId); return $after; }); } public function createCalendarYear($year, $userId) { $year = (int) $year; if ($year < 2000 || $year > 2200) { throw new BusinessException('Tahun buku tidak valid.'); } return $this->CI->transactionservice->run(function () use ($year, $userId) { $start = $year . '-01-01'; $end = $year . '-12-31'; $overlap = $this->CI->db ->where('start_date <=', $end) ->where('end_date >=', $start) ->count_all_results('fiscal_years'); if ($overlap > 0) { throw new BusinessException('Tahun buku tersebut sudah tersedia atau bertumpang tindih.'); } $this->CI->db->insert('fiscal_years', array( 'name' => 'Tahun Buku ' . $year, 'start_date' => $start, 'end_date' => $end, 'status' => 'open', 'created_at' => date('Y-m-d H:i:s'), 'created_by' => (int) $userId )); $fiscalId = (int) $this->CI->db->insert_id(); for ($month = 1; $month <= 12; $month++) { $periodStart = sprintf('%04d-%02d-01', $year, $month); $this->CI->db->insert('accounting_periods', array( 'fiscal_year_id' => $fiscalId, 'period_number' => $month, 'name' => date('F Y', strtotime($periodStart)), 'start_date' => $periodStart, 'end_date' => date('Y-m-t', strtotime($periodStart)), 'status' => 'open' )); } $this->CI->auditservice->record('fiscal_year','fiscal_year',$fiscalId,'create',null,array('year'=>$year),$userId); return $fiscalId; }); } }