150 lines
6.5 KiB
PHP
150 lines
6.5 KiB
PHP
<?php
|
|
defined('BASEPATH') OR exit('No direct script access allowed');
|
|
|
|
require_once APPPATH . 'exceptions/BusinessException.php';
|
|
|
|
class FiscalPeriodService
|
|
{
|
|
private $CI;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->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;
|
|
});
|
|
}
|
|
}
|