load->library('ProfessionalInventoryService');} public function negative_opening(){if(strtolower((string)app_env('APP_ENV',ENVIRONMENT))==='production'){echo"Ditolak: repair khusus Development.\n";return;}$rows=$this->db->query("SELECT item_id,warehouse_id,SUM(IF(direction='in',qty,-qty)) qty FROM inventory_ledger GROUP BY item_id,warehouse_id HAVING qty<-.0001")->result();$user=$this->db->select('users.id')->from('users')->join('roles','roles.id=users.role_id')->where('roles.nama_role','Admin')->order_by('users.id')->get()->row();if(!$user)throw new RuntimeException('User Admin tidak tersedia.');foreach($rows as$r){$item=$this->db->get_where('items',array('id'=>$r->item_id))->row();$key='DEV-NEGATIVE-OPENING-'.$r->item_id.'-'.$r->warehouse_id;$id=$this->professionalinventoryservice->createDocument(array('type'=>'opening','date'=>date('Y-m-d'),'from_warehouse_id'=>null,'to_warehouse_id'=>$r->warehouse_id,'item_id'=>$r->item_id,'barcode_id'=>null,'batch_id'=>null,'from_bin_id'=>null,'to_bin_id'=>null,'qty'=>abs($r->qty),'unit_cost'=>$item->harga_beli,'reason'=>'Koreksi saldo negatif legacy hasil backfill Tahap 7','idempotency_key'=>$key),$user->id);$doc=$this->db->get_where('stock_documents',array('id'=>$id))->row();if($doc->status==='draft')$this->professionalinventoryservice->submit($id,$user->id);$doc=$this->db->get_where('stock_documents',array('id'=>$id))->row();if($doc->status==='approved')$this->professionalinventoryservice->post($id,$user->id);echo'Corrected item '.$r->item_id.' warehouse '.$r->warehouse_id.' qty '.abs($r->qty)."\n";}} public function reconcile_gl(){if(strtolower((string)app_env('APP_ENV',ENVIRONMENT))==='production'){echo"Ditolak: repair khusus Development.\n";return;}$existing=$this->db->where('ref_type','inventory_reconciliation')->where('ref_code','DEV-STAGE7-BASELINE')->get('journals')->row();if($existing){echo"Baseline reconciliation sudah tersedia.\n";return;}$ledger=(float)$this->db->query("SELECT COALESCE(SUM(IF(direction='in',value,-value)),0) total FROM inventory_ledger")->row()->total;$inventory=$this->db->get_where('system_account_mappings',array('mapping_key'=>'inventory','is_active'=>1))->row()->account_id;$gl=(float)$this->db->query("SELECT COALESCE(SUM(d.debit-d.kredit),0) total FROM journal_details d JOIN journals j ON j.id=d.journal_id WHERE d.account_id=? AND j.status IN('posted','reversed')",array($inventory))->row()->total;$diff=round($ledger-$gl,2);if(abs($diff)<.01){echo"Ledger dan GL sudah balance.\n";return;}$user=$this->db->select('users.id')->from('users')->join('roles','roles.id=users.role_id')->where('roles.nama_role','Admin')->order_by('users.id')->get()->row();$other=$this->db->get_where('system_account_mappings',array('mapping_key'=>$diff>0?'inventory_adjustment_gain':'inventory_adjustment_loss','is_active'=>1))->row()->account_id;$this->load->library('PostingService');$lines=$diff>0?array(array('account_id'=>$inventory,'debit'=>$diff,'kredit'=>0),array('account_id'=>$other,'debit'=>0,'kredit'=>$diff)):array(array('account_id'=>$other,'debit'=>abs($diff),'kredit'=>0),array('account_id'=>$inventory,'debit'=>0,'kredit'=>abs($diff)));$id=$this->postingservice->post(array('tanggal'=>date('Y-m-d'),'no_ref'=>'INV-RECON-STAGE7-'.date('Ymd'),'keterangan'=>'Baseline rekonsiliasi inventory ledger legacy Tahap 7','ref_type'=>'inventory_reconciliation','ref_id'=>1,'created_by'=>$user->id),$lines,true,true);$this->db->where('id',$id)->update('journals',array('ref_code'=>'DEV-STAGE7-BASELINE'));$this->db->insert('inventory_reconciliations',array('as_of_date'=>date('Y-m-d'),'ledger_value'=>$ledger,'gl_value'=>$ledger,'difference'=>0,'status'=>'resolved','notes'=>'Development baseline; selisih awal '.number_format($diff,2,'.',''),'created_by'=>$user->id,'created_at'=>date('Y-m-d H:i:s')));echo'Jurnal rekonsiliasi dibuat: '.number_format($diff,2,'.','')."\n";} public function sync_cache(){if(strtolower((string)app_env('APP_ENV',ENVIRONMENT))==='production'){echo"Ditolak: repair khusus Development.\n";return;}$rows=$this->db->query("SELECT i.id,COALESCE(SUM(IF(l.direction='in',l.qty,-l.qty)),0) qty FROM items i LEFT JOIN inventory_ledger l ON l.item_id=i.id GROUP BY i.id")->result();$changed=0;foreach($rows as$r){$item=$this->db->get_where('items',array('id'=>$r->id))->row();if(abs((float)$item->stok-(float)$r->qty)>.0001){$this->db->where('id',$r->id)->update('items',array('stok'=>$r->qty));$changed++;}}echo"Selesai: {$changed} cache stok disinkronkan.\n";} }