db->query("SELECT COUNT(*) total_assets, SUM(workflow_status='capitalized' AND lifecycle_status IN('active','fully_depreciated')) active_assets, COALESCE(SUM(IF(workflow_status='capitalized' AND lifecycle_status NOT IN('disposed','lost','damaged'),nilai_perolehan,0)),0) acquisition_cost, COALESCE(SUM(IF(workflow_status='capitalized' AND lifecycle_status NOT IN('disposed','lost','damaged'),akumulasi_penyusutan,0)),0) accumulated_depreciation, COALESCE(SUM(IF(workflow_status='capitalized' AND lifecycle_status NOT IN('disposed','lost','damaged'),nilai_buku,0)),0) book_value, SUM(lifecycle_status='fully_depreciated') fully_depreciated, SUM(workflow_status IN('submitted','approved')) pending_workflow FROM assets WHERE company_id=?", array((int) $companyId))->row(); $row->maintenance_due = (int) $this->db->where('company_id', (int) $companyId)->where('status', 'posted')->where('next_due_date IS NOT NULL', null, false)->where('next_due_date <=', date('Y-m-d', strtotime('+30 days')))->count_all_results('asset_maintenance'); $row->open_opnames = (int) $this->db->where('company_id', (int) $companyId)->where_in('status', array('draft','submitted','approved'))->count_all_results('asset_opnames'); $row->depreciation_due = (int) $this->db->where('company_id', (int) $companyId)->where('workflow_status', 'capitalized')->where('lifecycle_status', 'active')->where('nilai_buku > nilai_residu', null, false)->count_all_results('assets'); return $row; } private function registerQuery($companyId, array $filters, $search = '') { $this->db->select('a.id,a.document_no,a.kode_asset,a.nama_asset,a.tanggal_perolehan,a.capitalization_date,a.source_type,a.workflow_status,a.lifecycle_status,a.nilai_perolehan,a.akumulasi_penyusutan,a.nilai_buku,a.nilai_residu,a.masa_manfaat,a.qty,a.qr_code,c.name category_name,l.nama location_name,e.full_name responsible_name,i.kode_detail,b.barcode,b.serial_number'); $this->db->from('assets a'); $this->db->join('asset_categories c', 'c.id=a.category_id', 'left'); $this->db->join('lokasi_asset l', 'l.id=a.lokasi_asset_id', 'left'); $this->db->join('k_employees e', 'e.id=a.responsible_employee_id', 'left'); $this->db->join('items i', 'i.id=a.item_id', 'left'); $this->db->join('item_barcodes b', 'b.id=CAST(a.barcode_id AS UNSIGNED)', 'left', false); $this->db->where('a.company_id', (int) $companyId); foreach (array('category_id','lokasi_asset_id','source_type','workflow_status','lifecycle_status','responsible_employee_id') as $field) { if (!empty($filters[$field])) $this->db->where('a.'.$field, $filters[$field]); } if (!empty($filters['date_from'])) $this->db->where('a.tanggal_perolehan >=', $filters['date_from']); if (!empty($filters['date_to'])) $this->db->where('a.tanggal_perolehan <=', $filters['date_to']); if ($search !== '') { $this->db->group_start()->like('a.document_no', $search)->or_like('a.kode_asset', $search)->or_like('a.nama_asset', $search)->or_like('c.name', $search)->or_like('l.nama', $search)->or_like('e.full_name', $search)->or_like('b.barcode', $search)->or_like('b.serial_number', $search)->group_end(); } } public function registerPage($companyId, array $filters, $start, $length, $search) { $this->registerQuery($companyId, $filters, $search); $filtered = (int) $this->db->count_all_results('', false); $rows = $this->db->order_by('a.id', 'DESC')->limit((int) $length, (int) $start)->get()->result(); $total = (int) $this->db->where('company_id', (int) $companyId)->count_all_results('assets'); return array('rows'=>$rows,'total'=>$total,'filtered'=>$filtered); } public function detail($id, $companyId) { $asset = $this->db->select('a.*,c.name category_name,c.code category_code,l.nama location_name,l.code location_code,e.full_name responsible_name,i.kode_detail,i.nama_barang,w.nama warehouse_name,bin.code bin_code,b.barcode,b.serial_number,uc.nama creator_name,us.nama submitter_name,ua.nama approver_name,up.nama capitalizer_name,j.no_ref source_journal_no,aj.no_ref acquisition_journal_no') ->from('assets a')->join('asset_categories c','c.id=a.category_id','left')->join('lokasi_asset l','l.id=a.lokasi_asset_id','left')->join('k_employees e','e.id=a.responsible_employee_id','left')->join('items i','i.id=a.item_id','left')->join('warehouses w','w.id=a.warehouse_id','left')->join('warehouse_bins bin','bin.id=a.bin_id','left')->join('item_barcodes b','b.id=CAST(a.barcode_id AS UNSIGNED)','left',false)->join('users uc','uc.id=a.created_by','left')->join('users us','us.id=a.submitted_by','left')->join('users ua','ua.id=a.approved_by','left')->join('users up','up.id=a.capitalized_by','left')->join('journals j','j.id=a.source_journal_id','left')->join('journals aj','aj.id=a.acquisition_journal_id','left')->where(array('a.id'=>(int)$id,'a.company_id'=>(int)$companyId))->get()->row(); if (!$asset) return null; $asset->events = $this->db->select('ev.*,u.nama user_name,fl.nama from_location,tl.nama to_location,j.no_ref journal_no')->from('asset_events ev')->join('users u','u.id=ev.created_by','left')->join('lokasi_asset fl','fl.id=ev.from_location_id','left')->join('lokasi_asset tl','tl.id=ev.to_location_id','left')->join('journals j','j.id=ev.journal_id','left')->where(array('ev.asset_id'=>$asset->id,'ev.company_id'=>(int)$companyId))->order_by('ev.event_date','DESC')->order_by('ev.id','DESC')->get()->result(); $asset->schedule = $this->db->where(array('asset_id'=>$asset->id,'company_id'=>(int)$companyId))->order_by('period','DESC')->get('asset_depreciation_schedule')->result(); $asset->maintenance = $this->db->where(array('asset_id'=>$asset->id,'company_id'=>(int)$companyId))->order_by('maintenance_date','DESC')->get('asset_maintenance')->result(); $asset->workflow = $this->db->select('h.*,u.nama user_name')->from('asset_workflow_history h')->join('users u','u.id=h.user_id','left')->where(array('h.asset_id'=>$asset->id,'h.company_id'=>(int)$companyId))->order_by('h.id')->get()->result(); $asset->responsibilities = $this->db->select('h.*,f.full_name from_name,t.full_name to_name,u.nama creator_name')->from('asset_responsibility_history h')->join('k_employees f','f.id=h.from_employee_id','left')->join('k_employees t','t.id=h.to_employee_id','left')->join('users u','u.id=h.created_by','left')->where(array('h.asset_id'=>$asset->id,'h.company_id'=>(int)$companyId))->order_by('h.effective_date','DESC')->get()->result(); $asset->attachments = $this->db->where(array('entity_type'=>'asset','entity_id'=>$asset->id,'company_id'=>(int)$companyId))->order_by('id')->get('transaction_attachments')->result(); return $asset; } public function categories($companyId, $activeOnly = false) { $this->db->where('company_id', (int) $companyId); if ($activeOnly) $this->db->where('is_active', 1); return $this->db->order_by('name')->get('asset_categories')->result(); } public function locations($companyId, $activeOnly = false) { $this->db->where('company_id', (int) $companyId); if ($activeOnly) $this->db->where('is_active', 1); return $this->db->order_by('nama')->get('lokasi_asset')->result(); } public function availableInventory($companyId, $search, $page = 1, $limit = 20) { $offset = (max(1, (int) $page)-1) * (int) $limit; $params = array((int) $companyId); $where = ''; if (trim($search) !== '') { $where = ' AND (i.kode_detail LIKE ? OR i.nama_barang LIKE ? OR b.barcode LIKE ? OR b.serial_number LIKE ?)'; $needle = '%'.trim($search).'%'; array_push($params, $needle, $needle, $needle, $needle); } $params[] = (int) $limit + 1; $params[] = $offset; return $this->db->query("SELECT b.id barcode_id,b.item_id,b.barcode,b.serial_number,b.warehouse_id,b.bin_id,b.batch_id,b.qty_sisa,b.reserved_qty,b.return_reserved_qty,i.kode_detail,i.nama_barang,i.harga_beli,k.tracking_type,k.need_serial_number,w.nama warehouse_name,wb.code bin_code,bt.batch_no, GREATEST(b.qty_sisa-b.reserved_qty-b.return_reserved_qty,0) available_qty, COALESCE((SELECT SUM(IF(il.direction='in',il.value,-il.value))/NULLIF(SUM(IF(il.direction='in',il.qty,-il.qty)),0) FROM inventory_ledger il WHERE il.item_id=b.item_id AND il.warehouse_id=b.warehouse_id),i.harga_beli,0) ledger_unit_cost FROM item_barcodes b JOIN items i ON i.id=b.item_id LEFT JOIN kode_barang k ON k.id=i.kode_id LEFT JOIN warehouses w ON w.id=b.warehouse_id LEFT JOIN warehouse_bins wb ON wb.id=b.bin_id LEFT JOIN inventory_batches bt ON bt.id=b.batch_id WHERE i.company_id=? AND i.status='active' AND b.status='available' AND b.warehouse_id IS NOT NULL AND b.qty_sisa-b.reserved_qty-b.return_reserved_qty>0 {$where} ORDER BY i.nama_barang,b.barcode LIMIT ? OFFSET ?", $params)->result(); } public function employees($companyId, $search, $page = 1, $limit = 20) { $this->db->select('id,employee_code,full_name')->where(array('company_id'=>(int)$companyId,'is_active'=>1)); if (trim($search) !== '') $this->db->group_start()->like('employee_code',$search)->or_like('full_name',$search)->group_end(); return $this->db->order_by('full_name')->limit((int)$limit+1,(max(1,(int)$page)-1)*(int)$limit)->get('k_employees')->result(); } public function accounts($companyId) { return $this->db->select('id,kode_akun,nama_akun,tipe,sub_tipe') ->where(array('company_id'=>(int)$companyId,'is_active'=>1,'allow_posting'=>1)) ->order_by('kode_akun')->get('accounts')->result(); } public function opnames($companyId) { return $this->db->select('o.*,l.nama location_name,u.nama creator_name,a.nama approver_name')->from('asset_opnames o')->join('lokasi_asset l','l.id=o.location_id','left')->join('users u','u.id=o.created_by','left')->join('users a','a.id=o.approved_by','left')->where('o.company_id',(int)$companyId)->order_by('o.id','DESC')->limit(200)->get()->result(); } public function opnameDetail($id, $companyId) { $row=$this->db->select('o.*,l.nama location_name,u.nama creator_name,s.nama submitter_name,a.nama approver_name,p.nama poster_name')->from('asset_opnames o')->join('lokasi_asset l','l.id=o.location_id','left')->join('users u','u.id=o.created_by','left')->join('users s','s.id=o.submitted_by','left')->join('users a','a.id=o.approved_by','left')->join('users p','p.id=o.posted_by','left')->where(array('o.id'=>(int)$id,'o.company_id'=>(int)$companyId))->get()->row(); if(!$row)return null; $row->lines=$this->db->select('ol.*,a.kode_asset,a.nama_asset,el.nama expected_location,al.nama actual_location')->from('asset_opname_lines ol')->join('assets a','a.id=ol.asset_id')->join('lokasi_asset el','el.id=ol.expected_location_id','left')->join('lokasi_asset al','al.id=ol.actual_location_id','left')->where(array('ol.asset_opname_id'=>$row->id,'ol.company_id'=>(int)$companyId))->order_by('a.kode_asset')->get()->result(); $row->attachments=$this->db->where(array('entity_type'=>'asset_opname','entity_id'=>$row->id,'company_id'=>(int)$companyId))->order_by('id')->get('transaction_attachments')->result(); return$row; } public function reconciliation($companyId, $asOf) { $costRows=$this->db->query("SELECT a.asset_account_id account_id,ac.kode_akun,ac.nama_akun,SUM(a.nilai_perolehan) subledger,COALESCE((SELECT SUM(jd.debit-jd.kredit) FROM journal_details jd JOIN journals j ON j.id=jd.journal_id WHERE j.company_id=? AND j.status='posted' AND j.tanggal<=? AND jd.account_id=a.asset_account_id),0) gl_value FROM assets a LEFT JOIN accounts ac ON ac.id=a.asset_account_id WHERE a.company_id=? AND a.workflow_status='capitalized' AND a.lifecycle_status NOT IN('disposed','lost','damaged') AND a.capitalization_date<=? GROUP BY a.asset_account_id,ac.kode_akun,ac.nama_akun",array((int)$companyId,$asOf,(int)$companyId,$asOf))->result(); $accRows=$this->db->query("SELECT a.accumulated_depreciation_account_id account_id,ac.kode_akun,ac.nama_akun,SUM(a.akumulasi_penyusutan) subledger,COALESCE((SELECT SUM(jd.kredit-jd.debit) FROM journal_details jd JOIN journals j ON j.id=jd.journal_id WHERE j.company_id=? AND j.status='posted' AND j.tanggal<=? AND jd.account_id=a.accumulated_depreciation_account_id),0) gl_value FROM assets a LEFT JOIN accounts ac ON ac.id=a.accumulated_depreciation_account_id WHERE a.company_id=? AND a.workflow_status='capitalized' AND a.lifecycle_status NOT IN('disposed','lost','damaged') AND a.capitalization_date<=? GROUP BY a.accumulated_depreciation_account_id,ac.kode_akun,ac.nama_akun",array((int)$companyId,$asOf,(int)$companyId,$asOf))->result(); $costSub=$costGl=$accSub=$accGl=0.0;foreach($costRows as$r){$costSub+=(float)$r->subledger;$costGl+=(float)$r->gl_value;$r->difference=(float)$r->subledger-(float)$r->gl_value;}foreach($accRows as$r){$accSub+=(float)$r->subledger;$accGl+=(float)$r->gl_value;$r->difference=(float)$r->subledger-(float)$r->gl_value;} return array('as_of'=>$asOf,'cost_subledger'=>$costSub,'cost_gl'=>$costGl,'cost_difference'=>$costSub-$costGl,'accumulated_subledger'=>$accSub,'accumulated_gl'=>$accGl,'accumulated_difference'=>$accSub-$accGl,'book_value'=>$costSub-$accSub,'status'=>abs($costSub-$costGl)<.01&&abs($accSub-$accGl)<.01?'matched':'variance','cost_details'=>$costRows,'accumulated_details'=>$accRows); } public function report($companyId, $type, array $filters) { if ($type === 'depreciation') return $this->db->select('s.period,a.kode_asset,a.nama_asset,s.opening_book_value,s.depreciation_amount,s.closing_book_value,s.status,j.no_ref journal_no')->from('asset_depreciation_schedule s')->join('assets a','a.id=s.asset_id')->join('journals j','j.id=s.journal_id','left')->where('s.company_id',(int)$companyId)->where('s.due_date >=',$filters['date_from'])->where('s.due_date <=',$filters['date_to'])->order_by('s.period','DESC')->order_by('a.kode_asset')->get()->result(); if ($type === 'maintenance') return $this->db->select('m.maintenance_no,m.maintenance_date,m.next_due_date,a.kode_asset,a.nama_asset,m.vendor,m.description,m.cost,m.status')->from('asset_maintenance m')->join('assets a','a.id=m.asset_id')->where('m.company_id',(int)$companyId)->where('m.maintenance_date >=',$filters['date_from'])->where('m.maintenance_date <=',$filters['date_to'])->order_by('m.maintenance_date','DESC')->get()->result(); if ($type === 'events') return $this->db->select('e.event_no,e.event_date,e.event_type,a.kode_asset,a.nama_asset,e.amount,e.book_value_before,e.book_value_after,e.notes,j.no_ref journal_no')->from('asset_events e')->join('assets a','a.id=e.asset_id')->join('journals j','j.id=e.journal_id','left')->where('e.company_id',(int)$companyId)->where('e.event_date >=',$filters['date_from'])->where('e.event_date <=',$filters['date_to'])->order_by('e.event_date','DESC')->order_by('e.id','DESC')->get()->result(); $page=$this->registerPage($companyId,$filters,0,10000,'');return$page['rows']; } }