Update besar apk finance

This commit is contained in:
Wian Drs
2026-09-11 16:03:00 +07:00
parent d42483b350
commit 4ef15c22ae
897 changed files with 98056 additions and 15979 deletions
+100
View File
@@ -0,0 +1,100 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Fixedassets extends MY_Controller
{
public function __construct()
{
parent::__construct();
$this->load->library(array('FixedAssetService','CompanyContext'));
$this->load->model('FixedAssetModel','fixedasset');
}
private function uid(){return(int)$this->session->userdata('user_id');}
private function company(){return(int)$this->companycontext->id();}
private function master(){return$this->session->userdata('role')==='Admin';}
private function can($action){return$this->master()||check_permission('asset',$action);}
private function permissions(){return array('create'=>$this->can('can_create'),'update'=>$this->can('can_update'),'approve'=>$this->can('can_approve'),'post'=>$this->can('can_post'),'export'=>$this->can('can_export'));}
private function postOnly(){if(strtoupper($this->input->method())!=='POST')show_404();}
private function action(callable$callback,$message){$this->postOnly();try{$result=$callback();return json_response(true,$message,array('result'=>$result));}catch(Throwable$e){return business_exception_response($e);}}
private function money($value){$value=preg_replace('/[^0-9,.-]/','',(string)$value);if(strpos($value,',')!==false){$value=str_replace('.','',$value);$value=str_replace(',','.',$value);}elseif(preg_match('/^-?\d{1,3}(\.\d{3})+$/',$value)){$value=str_replace('.','',$value);}return round((float)$value,2);}
public function index()
{
$data=array('active_menu'=>'fixed_assets','summary'=>$this->fixedasset->dashboard($this->company()),'categories'=>$this->fixedasset->categories($this->company()),'locations'=>$this->fixedasset->locations($this->company()),'accounts'=>$this->fixedasset->accounts($this->company()),'opnames'=>$this->fixedasset->opnames($this->company()),'asset_permissions'=>$this->permissions());
$this->load->view('partials/header',$data);$this->load->view('fixed_assets/index',$data);$this->load->view('partials/footer');
}
public function data()
{
$draw=(int)$this->input->get('draw');$start=max(0,(int)$this->input->get('start'));$length=min(100,max(10,(int)$this->input->get('length')));$search=$this->input->get('search');$search=is_array($search)?trim((string)($search['value']??'')):'';$filters=array('category_id'=>$this->input->get('category_id'),'lokasi_asset_id'=>$this->input->get('location_id'),'source_type'=>$this->input->get('source_type'),'workflow_status'=>$this->input->get('workflow_status'),'lifecycle_status'=>$this->input->get('lifecycle_status'),'responsible_employee_id'=>$this->input->get('responsible_employee_id'),'date_from'=>$this->input->get('date_from'),'date_to'=>$this->input->get('date_to'));$page=$this->fixedasset->registerPage($this->company(),$filters,$start,$length,$search);return$this->output->set_content_type('application/json')->set_output(json_encode(array('draw'=>$draw,'recordsTotal'=>$page['total'],'recordsFiltered'=>$page['filtered'],'data'=>$page['rows'])));
}
public function detail($id)
{
$asset=$this->fixedasset->detail((int)$id,$this->company());if(!$asset)return json_response(false,'Aset tidak ditemukan.',array(),404);foreach($asset->attachments as$file){$file->preview_url=base_url('accountingattachments/preview/'.$file->id);$file->download_url=base_url('accountingattachments/download/'.$file->id);}return json_response(true,'Detail aset.',array('asset'=>$asset,'permissions'=>$this->permissions()));
}
private function draftInput()
{
return array('nama_asset'=>trim((string)$this->input->post('nama_asset',true)),'tanggal_perolehan'=>$this->input->post('tanggal_perolehan',true),'capitalization_date'=>$this->input->post('capitalization_date',true),'category_id'=>(int)$this->input->post('category_id'),'lokasi_asset_id'=>(int)$this->input->post('lokasi_asset_id'),'responsible_employee_id'=>(int)$this->input->post('responsible_employee_id'),'source_type'=>$this->input->post('source_type',true),'source_id'=>(int)$this->input->post('source_id'),'source_reference_no'=>$this->input->post('source_reference_no',true),'supplier_id'=>(int)$this->input->post('supplier_id'),'source_journal_mode'=>$this->input->post('source_journal_mode',true),'source_journal_id'=>(int)$this->input->post('source_journal_id'),'item_id'=>(int)$this->input->post('item_id'),'barcode_id'=>(int)$this->input->post('barcode_id'),'warehouse_id'=>(int)$this->input->post('warehouse_id'),'bin_id'=>(int)$this->input->post('bin_id'),'qty'=>$this->money($this->input->post('qty')),'nilai_perolehan'=>$this->money($this->input->post('nilai_perolehan')),'nilai_residu'=>$this->money($this->input->post('nilai_residu')),'masa_manfaat'=>(int)$this->input->post('masa_manfaat'),'depreciation_method'=>$this->input->post('depreciation_method',true),'asset_account_id'=>(int)$this->input->post('asset_account_id'),'accumulated_depreciation_account_id'=>(int)$this->input->post('accumulated_depreciation_account_id'),'depreciation_expense_account_id'=>(int)$this->input->post('depreciation_expense_account_id'),'account_kredit_id'=>(int)$this->input->post('account_kredit_id'),'department_id'=>(int)$this->input->post('department_id'),'project_id'=>(int)$this->input->post('project_id'),'cost_center_id'=>(int)$this->input->post('cost_center_id'),'keterangan'=>$this->input->post('keterangan',true),'idempotency_key'=>$this->input->post('idempotency_key',true));
}
public function save()
{
$id=(int)$this->input->post('id');$this->requirePermission('asset',$id?'can_update':'can_create');return$this->action(function()use($id){return$id?$this->fixedassetservice->updateDraft($id,$this->draftInput(),$this->uid()):$this->fixedassetservice->createDraft($this->draftInput(),$this->uid());},'Draft perolehan aset berhasil disimpan.');
}
public function submit($id){$this->requirePermission('asset','can_create');return$this->action(fn()=>$this->fixedassetservice->submit($id,$this->uid()),'Dokumen aset berhasil diajukan.');}
public function approve($id){$this->requirePermission('asset','can_approve');return$this->action(fn()=>$this->fixedassetservice->approve($id,$this->uid(),$this->master()),'Dokumen aset berhasil disetujui.');}
public function reject($id){$this->requirePermission('asset','can_approve');return$this->action(fn()=>$this->fixedassetservice->reject($id,$this->input->post('reason',true),$this->uid(),$this->master()),'Dokumen aset ditolak.');}
public function reopen($id){$this->requirePermission('asset','can_update');return$this->action(fn()=>$this->fixedassetservice->reopenRejected($id,$this->uid()),'Dokumen dikembalikan menjadi draft.');}
public function capitalize($id){$this->requirePermission('asset','can_post');return$this->action(fn()=>$this->fixedassetservice->capitalize($id,$this->input->post('date',true),$this->uid()),'Aset berhasil dikapitalisasi.');}
public function depreciate($id){$this->requirePermission('asset','can_post');return$this->action(fn()=>$this->fixedassetservice->depreciate($id,$this->input->post('through_date',true),$this->uid(),true),'Penyusutan aset berhasil diproses.');}
public function mutate($id){$this->requirePermission('asset','can_post');return$this->action(fn()=>$this->fixedassetservice->mutate($id,$this->input->post('type',true),$this->input->post('date',true),$this->money($this->input->post('amount')),(int)$this->input->post('account_id'),$this->input->post('notes',true),$this->uid()),'Mutasi nilai aset berhasil diposting.');}
public function transfer($id){$this->requirePermission('asset','can_update');return$this->action(fn()=>$this->fixedassetservice->transfer($id,$this->input->post('date',true),(int)$this->input->post('location_id'),$this->input->post('notes',true),$this->uid()),'Lokasi aset berhasil dipindahkan.');}
public function responsibility($id){$this->requirePermission('asset','can_update');return$this->action(fn()=>$this->fixedassetservice->changeResponsibility($id,$this->input->post('date',true),(int)$this->input->post('employee_id'),$this->input->post('notes',true),$this->uid()),'Penanggung jawab aset berhasil diperbarui.');}
public function maintenance($id){$this->requirePermission('asset','can_update');return$this->action(fn()=>$this->fixedassetservice->maintenance($id,array('date'=>$this->input->post('date',true),'vendor'=>$this->input->post('vendor',true),'description'=>$this->input->post('description',true),'cost'=>$this->money($this->input->post('cost')),'next_due_date'=>$this->input->post('next_due_date',true),'idempotency_key'=>$this->input->post('idempotency_key',true)),$this->uid()),'Maintenance aset berhasil dicatat.');}
public function reverse($eventId){$this->requirePermission('asset','can_post');return$this->action(fn()=>$this->fixedassetservice->reverseEvent($eventId,$this->input->post('date',true),$this->input->post('reason',true),$this->uid()),'Event aset berhasil direversal.');}
public function lookup_inventory()
{
$page=max(1,(int)$this->input->get('page'));$limit=20;$rows=$this->fixedasset->availableInventory($this->company(),$this->input->get('q',true),$page,$limit);$more=count($rows)>$limit;if($more)array_pop($rows);$result=array();foreach($rows as$row)$result[]=array('id'=>$row->barcode_id,'text'=>$row->kode_detail.' - '.$row->nama_barang.' | '.$row->barcode.($row->serial_number?' / SN '.$row->serial_number:'').' | '.$row->warehouse_name.' | tersedia '.$this->qty($row->available_qty),'data'=>$row);return json_response(true,'Barang gudang tersedia.',array('results'=>$result,'pagination'=>array('more'=>$more)));
}
public function lookup_employees()
{
$page=max(1,(int)$this->input->get('page'));$limit=20;$rows=$this->fixedasset->employees($this->company(),$this->input->get('q',true),$page,$limit);$more=count($rows)>$limit;if($more)array_pop($rows);$result=array();foreach($rows as$row)$result[]=array('id'=>$row->id,'text'=>$row->employee_code.' - '.$row->full_name);return json_response(true,'Daftar karyawan.',array('results'=>$result,'pagination'=>array('more'=>$more)));
}
public function lookup_journals()
{
$q=trim((string)$this->input->get('q',true));$this->db->select("id,CONCAT(no_ref,' - ',DATE_FORMAT(tanggal,'%d/%m/%Y'),' - ',LEFT(keterangan,80)) text",false)->where(array('company_id'=>$this->company(),'status'=>'posted'));if($q!=='')$this->db->group_start()->like('no_ref',$q)->or_like('keterangan',$q)->group_end();$rows=$this->db->order_by('tanggal','DESC')->limit(25)->get('journals')->result();return json_response(true,'Daftar jurnal.',array('results'=>$rows));
}
public function master_data(){return json_response(true,'Master aset.',array('categories'=>$this->fixedasset->categories($this->company()),'locations'=>$this->fixedasset->locations($this->company())));}
public function save_category(){ $this->requirePermission('asset','can_update');return$this->action(fn()=>$this->fixedassetservice->saveCategory(array('id'=>$this->input->post('id'),'code'=>$this->input->post('code',true),'name'=>$this->input->post('name',true),'life'=>$this->input->post('life'),'residual_percent'=>$this->money($this->input->post('residual_percent')),'method'=>$this->input->post('method',true),'asset_account_id'=>$this->input->post('asset_account_id'),'accumulated_account_id'=>$this->input->post('accumulated_account_id'),'expense_account_id'=>$this->input->post('expense_account_id'),'is_active'=>$this->input->post('is_active')),$this->uid()),'Kategori aset berhasil disimpan.');}
public function save_location(){ $this->requirePermission('asset','can_update');return$this->action(fn()=>$this->fixedassetservice->saveLocation(array('id'=>$this->input->post('id'),'code'=>$this->input->post('code',true),'name'=>$this->input->post('name',true),'address'=>$this->input->post('address',true),'responsible_employee_id'=>$this->input->post('responsible_employee_id'),'is_active'=>$this->input->post('is_active')),$this->uid()),'Lokasi aset berhasil disimpan.');}
public function opname_create(){ $this->requirePermission('asset','can_create');return$this->action(fn()=>$this->fixedassetservice->createOpname((int)$this->input->post('location_id'),$this->input->post('date',true),$this->input->post('notes',true),$this->input->post('idempotency_key',true),$this->uid()),'Draft asset opname berhasil dibuat.');}
public function opname_detail($id){$row=$this->fixedasset->opnameDetail((int)$id,$this->company());if(!$row)return json_response(false,'Asset opname tidak ditemukan.',array(),404);foreach($row->attachments as$file)$file->preview_url=base_url('accountingattachments/preview/'.$file->id);return json_response(true,'Detail asset opname.',array('opname'=>$row,'permissions'=>$this->permissions()));}
public function opname_save($id){$this->requirePermission('asset','can_update');return$this->action(function()use($id){$lines=json_decode((string)$this->input->post('lines'),true);if(!is_array($lines))throw new BusinessException('Detail opname tidak valid.');return$this->fixedassetservice->saveOpnameLines($id,$lines,$this->uid());},'Hasil hitung fisik berhasil disimpan.');}
public function opname_state($id){$action=$this->input->post('action',true);$this->requirePermission('asset',in_array($action,array('approve','reject'),true)?'can_approve':($action==='post'?'can_post':'can_create'));return$this->action(fn()=>$this->fixedassetservice->opnameState($id,$action,$this->input->post('reason',true),$this->uid(),$this->master()),'Status asset opname berhasil diperbarui.');}
public function reconciliation()
{
$this->requirePermission('asset','can_view');$date=$this->input->get('date')?:date('Y-m-d');try{$result=$this->fixedasset->reconciliation($this->company(),$date);return json_response(true,'Rekonsiliasi aset selesai tanpa jurnal otomatis.',array('result'=>$result));}catch(Throwable$e){return business_exception_response($e);}
}
public function report_data(){try{$type=$this->input->get('type')?:'register';$filters=$this->reportFilters();if($type==='reconciliation')return json_response(true,'Laporan rekonsiliasi.',array('data'=>array($this->fixedasset->reconciliation($this->company(),$filters['date_to']))));return json_response(true,'Laporan aset.',array('data'=>$this->fixedasset->report($this->company(),$type,$filters)));}catch(Throwable$e){return business_exception_response($e);}}
private function reportFilters(){return array('category_id'=>$this->input->get('category_id'),'lokasi_asset_id'=>$this->input->get('location_id'),'source_type'=>$this->input->get('source_type'),'workflow_status'=>$this->input->get('workflow_status'),'lifecycle_status'=>$this->input->get('lifecycle_status'),'date_from'=>$this->input->get('date_from')?:date('Y-01-01'),'date_to'=>$this->input->get('date_to')?:date('Y-m-d'));}
private function reportDefinition($type){$all=array('register'=>array('title'=>'Asset Register','columns'=>array('kode_asset'=>'Kode','nama_asset'=>'Nama Aset','category_name'=>'Kategori','location_name'=>'Lokasi','source_type'=>'Sumber','tanggal_perolehan'=>'Perolehan','nilai_perolehan'=>'Nilai Perolehan','akumulasi_penyusutan'=>'Akumulasi','nilai_buku'=>'Nilai Buku','lifecycle_status'=>'Status')),'depreciation'=>array('title'=>'Schedule Penyusutan','columns'=>array('period'=>'Periode','kode_asset'=>'Kode','nama_asset'=>'Aset','opening_book_value'=>'Nilai Awal','depreciation_amount'=>'Penyusutan','closing_book_value'=>'Nilai Akhir','status'=>'Status','journal_no'=>'Jurnal')),'maintenance'=>array('title'=>'Riwayat Maintenance','columns'=>array('maintenance_no'=>'Dokumen','maintenance_date'=>'Tanggal','kode_asset'=>'Kode','nama_asset'=>'Aset','vendor'=>'Vendor','description'=>'Keterangan','cost'=>'Biaya','next_due_date'=>'Jadwal Berikutnya','status'=>'Status')),'events'=>array('title'=>'Mutasi dan Audit Aset','columns'=>array('event_no'=>'Dokumen','event_date'=>'Tanggal','event_type'=>'Jenis','kode_asset'=>'Kode','nama_asset'=>'Aset','amount'=>'Nilai','book_value_before'=>'Nilai Sebelum','book_value_after'=>'Nilai Sesudah','journal_no'=>'Jurnal','notes'=>'Keterangan')),'reconciliation'=>array('title'=>'Rekonsiliasi Aset dengan Buku Besar','columns'=>array('as_of'=>'Tanggal','cost_subledger'=>'Perolehan Subledger','cost_gl'=>'Akun Aset GL','cost_difference'=>'Selisih Aset','accumulated_subledger'=>'Akumulasi Subledger','accumulated_gl'=>'Akumulasi GL','accumulated_difference'=>'Selisih Akumulasi','book_value'=>'Nilai Buku','status'=>'Status')));if(!isset($all[$type]))throw new BusinessException('Jenis laporan aset tidak valid.');return$all[$type];}
public function report_pdf(){ $this->requirePermission('asset','can_export');$type=$this->input->get('type')?:'register';$filters=$this->reportFilters();$definition=$this->reportDefinition($type);$rows=$type==='reconciliation'?array((object)$this->fixedasset->reconciliation($this->company(),$filters['date_to'])):$this->fixedasset->report($this->company(),$type,$filters);$this->inlinePdf($this->load->view('fixed_assets/report_pdf',array('definition'=>$definition,'rows'=>$rows,'filters'=>$filters,'company'=>$this->companyRow()),true),'Laporan-Aset-'.$type.'-'.date('Ymd').'.pdf','landscape');}
public function report_excel(){ $this->requirePermission('asset','can_export');$type=$this->input->get('type')?:'register';$filters=$this->reportFilters();$definition=$this->reportDefinition($type);$rows=$type==='reconciliation'?array((object)$this->fixedasset->reconciliation($this->company(),$filters['date_to'])):$this->fixedasset->report($this->company(),$type,$filters);require_once FCPATH.'vendor/autoload.php';$book=new\PhpOffice\PhpSpreadsheet\Spreadsheet();$sheet=$book->getActiveSheet();$last=\PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex(count($definition['columns']));$company=$this->companyRow();$sheet->setCellValue('A1',$company->name??'Perusahaan')->mergeCells('A1:'.$last.'1');$sheet->setCellValue('A2',$definition['title'])->mergeCells('A2:'.$last.'2');$sheet->setCellValue('A3','Periode '.$filters['date_from'].' s.d. '.$filters['date_to'])->mergeCells('A3:'.$last.'3');$col=1;foreach($definition['columns']as$label)$sheet->setCellValueByColumnAndRow($col++,5,$label);$rowNo=6;foreach($rows as$row){$col=1;foreach($definition['columns']as$key=>$label)$sheet->setCellValueByColumnAndRow($col++,$rowNo,$row->$key??null);$rowNo++;}$sheet->getStyle('A1:'.$last.'2')->getFont()->setBold(true);$sheet->getStyle('A5:'.$last.'5')->getFont()->setBold(true);for($i=1;$i<=count($definition['columns']);$i++)$sheet->getColumnDimension(\PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($i))->setAutoSize(true);while(ob_get_level()>0)ob_end_clean();header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');header('Content-Disposition: attachment; filename="Laporan-Aset-'.$type.'-'.date('Ymd-His').'.xlsx"');\PhpOffice\PhpSpreadsheet\IOFactory::createWriter($book,'Xlsx')->save('php://output');exit;}
public function asset_pdf($id){$this->requirePermission('asset','can_export');$asset=$this->fixedasset->detail((int)$id,$this->company());if(!$asset)show_404();$this->inlinePdf($this->load->view('fixed_assets/asset_pdf',array('asset'=>$asset,'company'=>$this->companyRow(),'qr'=>$this->qrData(base_url('fixedassets/register/'.$asset->kode_asset))),true),'Kartu-Aset-'.$asset->kode_asset.'.pdf');}
public function opname_pdf($id){$this->requirePermission('asset','can_export');$opname=$this->fixedasset->opnameDetail((int)$id,$this->company());if(!$opname)show_404();$this->inlinePdf($this->load->view('fixed_assets/opname_pdf',array('opname'=>$opname,'company'=>$this->companyRow()),true),'Opname-'.$opname->opname_no.'.pdf','landscape');}
public function qr($id){$asset=$this->fixedasset->detail((int)$id,$this->company());if(!$asset)show_404();$this->output->set_content_type('text/html')->set_output($this->load->view('fixed_assets/qr_label',array('asset'=>$asset,'qr'=>$this->qrData(base_url('fixedassets/register/'.$asset->kode_asset))),true));}
public function register($code){$asset=$this->db->select('a.kode_asset,a.nama_asset,a.lifecycle_status,a.nilai_buku,l.nama location_name,e.full_name responsible_name')->from('assets a')->join('lokasi_asset l','l.id=a.lokasi_asset_id','left')->join('k_employees e','e.id=a.responsible_employee_id','left')->where(array('a.kode_asset'=>$code,'a.company_id'=>$this->company()))->get()->row();if(!$asset)return json_response(false,'Aset tidak ditemukan.',array(),404);return json_response(true,'Identitas aset terverifikasi.',array('asset'=>$asset));}
private function companyRow(){return$this->db->get_where('companies',array('id'=>$this->company()))->row();}
private function inlinePdf($html,$filename,$orientation='portrait'){require_once FCPATH.'vendor/autoload.php';$pdf=new Dompdf\Dompdf();$pdf->loadHtml($html);$pdf->setPaper('A4',$orientation);$pdf->render();$pdf->stream($filename,array('Attachment'=>false));}
private function qrData($text){require_once APPPATH.'libraries/phpqrcode/qrlib.php';ob_start();QRcode::png($text,null,QR_ECLEVEL_M,5,1);$png=ob_get_clean();return'data:image/png;base64,'.base64_encode($png);}
private function qty($value){$number=(float)$value;return number_format($number,abs($number-round($number))<.0001?0:4,',','.');}
}