CI =& get_instance(); $this->CI->load->model('JournalModel', 'workflowJournalModel'); $this->CI->load->library('TransactionService'); $this->CI->load->library('FiscalPeriodService'); $this->CI->load->library('NumberingService'); $this->CI->load->library('PostingService'); $this->CI->load->library('ApprovalService'); $this->CI->load->library('AuditService'); $this->CI->config->load('accounting', true); } public function submit($journalId, $userId) { return $this->CI->transactionservice->run(function () use ($journalId, $userId) { $journal = $this->lockedJournal($journalId, 'draft'); if ((int)$journal->created_by !== (int)$userId && $this->CI->session->userdata('role') !== 'Admin') throw new BusinessException('Anda hanya dapat mengajukan jurnal yang Anda buat.'); $this->CI->fiscalperiodservice->assertOpen($journal->tanggal); $lines=$this->CI->workflowJournalModel->lines($journal->id); $amount=0; foreach($lines as $line) $amount+=(float)$line['debit']; $this->CI->approvalservice->create('journal','journal',$journal->id,$amount,$userId,array('journal'=>$journal,'lines'=>$lines)); $this->CI->workflowJournalModel->updateStatus($journal->id,'submitted',array('submitted_at'=>date('Y-m-d H:i:s'),'submitted_by'=>(int)$userId)); $this->CI->workflowJournalModel->addStatusHistory($journal->id,'draft','submitted',$userId); $after=$this->CI->workflowJournalModel->find($journal->id); $this->CI->auditservice->record('journal','journal',$journal->id,'submit',$journal,$after,$userId); return $after; }); } public function approve($journalId, $userId) { return $this->CI->transactionservice->run(function () use ($journalId, $userId) { $journal = $this->lockedJournal($journalId, 'submitted'); $request=$this->CI->approvalservice->pendingForEntity('journal',$journal->id); if(!$request) throw new BusinessException('Permintaan approval jurnal tidak ditemukan.'); $roleId=$this->roleId($userId); $approval=$this->CI->approvalservice->act($request->id,'approved',$userId,$roleId); if($approval->status!=='approved') return $journal; $this->CI->fiscalperiodservice->assertOpen($journal->tanggal); $now=date('Y-m-d H:i:s'); $this->CI->workflowJournalModel->updateStatus($journal->id, 'posted', array( 'approved_at'=>$now,'approved_by'=>(int)$userId,'posted_at'=>$now,'posted_by'=>(int)$userId )); $this->CI->workflowJournalModel->addStatusHistory($journal->id, 'submitted', 'approved', $userId); $this->CI->workflowJournalModel->addStatusHistory($journal->id, 'approved', 'posted', $userId, 'Otomatis diposting setelah approval final.'); $after=$this->CI->workflowJournalModel->find($journal->id); $this->CI->auditservice->record('journal','journal',$journal->id,'approve_and_post',$journal,$after,$userId); return $after; }); } public function post($journalId, $userId) { return $this->CI->transactionservice->run(function () use ($journalId, $userId) { $journal = $this->lockedJournal($journalId, 'approved'); $this->CI->fiscalperiodservice->assertOpen($journal->tanggal); $this->CI->workflowJournalModel->updateStatus($journal->id, 'posted', array( 'posted_at' => date('Y-m-d H:i:s'), 'posted_by' => (int) $userId )); $this->CI->workflowJournalModel->addStatusHistory($journal->id, 'approved', 'posted', $userId); $after=$this->CI->workflowJournalModel->find($journal->id); $this->CI->auditservice->record('journal','journal',$journal->id,'post',$journal,$after,$userId); return $after; }); } public function reject($journalId, $userId, $reason) { $reason = trim((string) $reason); if ($reason === '') { throw new BusinessException('Alasan penolakan wajib diisi.'); } return $this->CI->transactionservice->run(function () use ($journalId, $userId, $reason) { $journal = $this->CI->workflowJournalModel->findForUpdate($journalId); if (!$journal || !in_array($journal->status, array('submitted', 'approved'), true)) { throw new BusinessException('Jurnal tidak berada pada status yang dapat ditolak.'); } if($journal->status==='submitted') { $request=$this->CI->approvalservice->pendingForEntity('journal',$journal->id); if($request) $this->CI->approvalservice->act($request->id,'rejected',$userId,$this->roleId($userId),$reason); } $this->CI->workflowJournalModel->updateStatus($journal->id, 'rejected', array( 'rejected_at' => date('Y-m-d H:i:s'), 'rejected_by' => (int) $userId, 'rejection_reason' => $reason )); $this->CI->workflowJournalModel->addStatusHistory($journal->id, $journal->status, 'rejected', $userId, $reason); $after=$this->CI->workflowJournalModel->find($journal->id); $this->CI->auditservice->record('journal','journal',$journal->id,'reject',$journal,$after,$userId); return $after; }); } public function reverse($journalId, $date, $userId, $reason) { $reason = trim((string) $reason); if ($reason === '') { throw new BusinessException('Alasan reversal wajib diisi.'); } return $this->CI->transactionservice->run(function () use ($journalId, $date, $userId, $reason) { $journal = $this->lockedJournal($journalId, 'posted'); if ((int) $journal->is_reversed === 1) { throw new BusinessException('Jurnal sudah pernah direversal.'); } $this->CI->fiscalperiodservice->assertOpen($date); $lines = array(); foreach ($this->CI->workflowJournalModel->lines($journal->id) as $line) { $lines[] = array('account_id' => $line['account_id'], 'debit' => $line['kredit'], 'kredit' => $line['debit']); } $number = $this->CI->numberingservice->next('reversal', $date); $reversalId = $this->CI->postingservice->post(array( 'tanggal' => $date, 'no_ref' => $number, 'keterangan' => 'Reversal ' . $journal->no_ref . ': ' . $reason, 'ref_type' => 'journal_reversal', 'ref_id' => $journal->id, 'reversal_of' => $journal->id, 'reversal_reason' => $reason, 'created_by' => (int) $userId ), $lines, false, true); $this->CI->workflowJournalModel->updateStatus($journal->id, 'reversed', array( 'is_reversed' => 1, 'reversal_reason' => $reason, 'updated_by' => (int) $userId )); $this->CI->workflowJournalModel->addStatusHistory($journal->id, 'posted', 'reversed', $userId, $reason); $this->CI->auditservice->record('journal','journal',$journal->id,'reverse',$journal,$this->CI->workflowJournalModel->find($journal->id),$userId); return array('journal' => $this->CI->workflowJournalModel->find($journal->id), 'reversal_id' => $reversalId, 'reversal_no' => $number); }); } private function transition($journalId, $from, $to, $userId, array $extra) { return $this->CI->transactionservice->run(function () use ($journalId, $from, $to, $userId, $extra) { $journal = $this->lockedJournal($journalId, $from); if ($from === 'draft' && (int) $journal->created_by !== (int) $userId && $this->CI->session->userdata('role') !== 'Admin') { throw new BusinessException('Anda hanya dapat mengajukan jurnal yang Anda buat.'); } $this->CI->fiscalperiodservice->assertOpen($journal->tanggal); $this->CI->workflowJournalModel->updateStatus($journal->id, $to, $extra); $this->CI->workflowJournalModel->addStatusHistory($journal->id, $from, $to, $userId); return $this->CI->workflowJournalModel->find($journal->id); }); } private function lockedJournal($journalId, $expectedStatus) { $journal = $this->CI->workflowJournalModel->findForUpdate($journalId); if (!$journal) { throw new BusinessException('Jurnal tidak ditemukan.', array(), 404); } if ($journal->status !== $expectedStatus) { throw new BusinessException('Jurnal harus berstatus ' . $expectedStatus . ' untuk tindakan ini.'); } return $journal; } private function roleId($userId) { $roleId=(int)$this->CI->session->userdata('role_id'); if(!$roleId) { $user=$this->CI->db->select('role_id')->get_where('users',array('id'=>(int)$userId))->row(); $roleId=$user?(int)$user->role_id:0; } return $roleId; } }