update di penjualan invoice

This commit is contained in:
Wian Drs
2026-09-12 01:45:03 +07:00
parent e43bda8d6b
commit ec94783db4
22 changed files with 776 additions and 44 deletions
+3 -2
View File
@@ -7,6 +7,7 @@ class Customers extends MY_Controller {
{
parent::__construct();
$this->load->model('DynamicModel', 'dm');
$this->load->library('SalesService');
if (!$this->session->userdata('logged_in')) {
redirect('auth');
@@ -15,9 +16,9 @@ class Customers extends MY_Controller {
public function index()
{
$data = ["active_menu" => "customers"];
$data = ["active_menu" => "customers", "summary" => $this->salesservice->dashboard()];
$this->load->view('partials/header', $data);
$this->load->view('customers/layout');
$this->load->view('customers/layout', $data);
$this->load->view('partials/footer');
}
+5 -3
View File
@@ -58,14 +58,16 @@ class Invoices extends MY_Controller
}
public function lookup_items()
{
$q=trim((string)$this->input->get('q'));$page=max(1,(int)$this->input->get('page'));$limit=20;$this->db->select("i.id,CONCAT(i.kode_detail,' - ',i.nama_barang) text,i.kode_detail,i.nama_barang,i.harga_jual,i.stok,k.tracking_type,k.unit",false)->from('items i')->join('kode_barang k','k.id=i.kode_id','left')->where(array('i.company_id'=>$this->company(),'i.status'=>'active','i.stok >'=>'0'));if($q!=='')$this->db->group_start()->like('i.kode_detail',$q)->or_like('i.nama_barang',$q)->group_end();$rows=$this->db->order_by('i.nama_barang')->limit($limit+1,($page-1)*$limit)->get()->result();$more=count($rows)>$limit;if($more)array_pop($rows);return json_response(true,'Barang ditemukan.',array('results'=>$rows,'pagination'=>array('more'=>$more)));
$q=trim((string)$this->input->get('q'));$page=max(1,(int)$this->input->get('page'));$limit=20;
$available="(SELECT ib.item_id,SUM(CASE WHEN ib.status IN('available','reserved') THEN GREATEST(ib.qty_sisa-COALESCE(ib.reserved_qty,0)-COALESCE(ib.return_reserved_qty,0),0) ELSE 0 END) available_stock FROM item_barcodes ib GROUP BY ib.item_id) av";
$this->db->select("i.id,CONCAT(i.kode_detail,' - ',i.nama_barang,' - tersedia ',FORMAT(COALESCE(av.available_stock,0),0,'id_ID')) text,i.kode_detail,i.nama_barang,i.harga_jual,COALESCE(av.available_stock,0) stok,k.tracking_type,k.unit",false)->from('items i')->join('kode_barang k','k.id=i.kode_id','left')->join($available,'av.item_id=i.id','inner',false)->where(array('i.company_id'=>$this->company(),'i.status'=>'active'))->where('av.available_stock >',0);if($q!=='')$this->db->group_start()->like('i.kode_detail',$q)->or_like('i.nama_barang',$q)->group_end();$rows=$this->db->order_by('i.nama_barang')->limit($limit+1,($page-1)*$limit)->get()->result();$more=count($rows)>$limit;if($more)array_pop($rows);return json_response(true,'Barang ditemukan.',array('results'=>$rows,'pagination'=>array('more'=>$more)));
}
public function lookup_warehouses(){return$this->selectLookup('warehouses','nama',array('nama'),'Gudang ditemukan.');}
public function lookup_barcodes()
{
$q=trim((string)$this->input->get('q'));$item=(int)$this->input->get('item_id');$warehouse=(int)$this->input->get('warehouse_id');$exact=(int)$this->input->get('exact')===1;$page=max(1,(int)$this->input->get('page'));$limit=20;
if(!$exact&&(!$item||!$warehouse))return json_response(true,'Pilih gudang dan barang terlebih dahulu.',array('results'=>array(),'pagination'=>array('more'=>false)));
$this->db->select("b.id,CONCAT(b.barcode,IF(NULLIF(b.serial_number,'') IS NULL,'',CONCAT(' / ',b.serial_number)),' - tersedia ',ROUND(b.qty_sisa-b.reserved_qty-COALESCE(b.return_reserved_qty,0),4)) text,b.barcode,b.serial_number,b.item_id,b.warehouse_id,(b.qty_sisa-b.reserved_qty-COALESCE(b.return_reserved_qty,0)) available,i.kode_detail,i.nama_barang,i.harga_jual,k.tracking_type,k.unit,w.nama warehouse_name",false)->from('item_barcodes b')->join('items i','i.id=b.item_id')->join('kode_barang k','k.id=i.kode_id','left')->join('warehouses w','w.id=b.warehouse_id')->where(array('i.company_id'=>$this->company(),'b.status'=>'available'))->where('(b.qty_sisa-b.reserved_qty-COALESCE(b.return_reserved_qty,0)) >',0,false);if($item)$this->db->where('b.item_id',$item);if($warehouse)$this->db->where('b.warehouse_id',$warehouse);if($q!==''){$this->db->group_start();if($exact)$this->db->where('b.barcode',$q)->or_where('b.serial_number',$q);else$this->db->like('b.barcode',$q)->or_like('b.serial_number',$q);$this->db->group_end();}$rows=$this->db->order_by('b.id')->limit($limit+1,($page-1)*$limit)->get()->result();$more=count($rows)>$limit;if($more)array_pop($rows);return json_response(true,'Barcode ditemukan.',array('results'=>$rows,'pagination'=>array('more'=>$more)));
$this->db->select("b.id,CONCAT(b.barcode,IF(NULLIF(b.serial_number,'') IS NULL,'',CONCAT(' / ',b.serial_number)),' - tersedia ',FORMAT(b.qty_sisa-b.reserved_qty-COALESCE(b.return_reserved_qty,0),0,'id_ID')) text,b.barcode,b.serial_number,b.item_id,b.warehouse_id,(b.qty_sisa-b.reserved_qty-COALESCE(b.return_reserved_qty,0)) available,i.kode_detail,i.nama_barang,i.harga_jual,k.tracking_type,k.unit,w.nama warehouse_name",false)->from('item_barcodes b')->join('items i','i.id=b.item_id')->join('kode_barang k','k.id=i.kode_id','left')->join('warehouses w','w.id=b.warehouse_id')->where(array('i.company_id'=>$this->company(),'b.status'=>'available'))->where('(b.qty_sisa-b.reserved_qty-COALESCE(b.return_reserved_qty,0)) >',0,false)->where("(UPPER(COALESCE(k.tracking_type,'QTY'))<>'UNIT' OR NOT EXISTS(SELECT 1 FROM invoice_line_barcodes used JOIN invoice_details used_detail ON used_detail.id=used.invoice_detail_id AND used_detail.stock_posted_at IS NULL JOIN invoices used_invoice ON used_invoice.id=used.invoice_id WHERE used.barcode_id=b.id AND used_invoice.company_id=i.company_id AND used_invoice.deleted_at IS NULL AND COALESCE(used_invoice.status,'')<>'cancelled'))",null,false);if($item)$this->db->where('b.item_id',$item);if($warehouse)$this->db->where('b.warehouse_id',$warehouse);if($q!==''){$this->db->group_start();if($exact)$this->db->where('b.barcode',$q)->or_where('b.serial_number',$q);else$this->db->like('b.barcode',$q)->or_like('b.serial_number',$q);$this->db->group_end();}$rows=$this->db->order_by('b.id')->limit($limit+1,($page-1)*$limit)->get()->result();$more=count($rows)>$limit;if($more)array_pop($rows);return json_response(true,'Barcode ditemukan.',array('results'=>$rows,'pagination'=>array('more'=>$more)));
}
private function selectLookup($table,$label,array$fields,$message)
{
@@ -107,7 +109,7 @@ class Invoices extends MY_Controller
public function submit($id){return$this->mutation('can_submit',function()use($id){return$this->salesservice->submitInvoice($id,$this->uid());},'Invoice berhasil diajukan.');}
public function approve($id){return$this->mutation('can_approve',function()use($id){return$this->salesservice->approveAndPostInvoice($id,$this->uid(),$this->roleId());},'Invoice disetujui dan otomatis diposting.');}
public function reject($id){return$this->mutation('can_approve',function()use($id){return$this->salesservice->rejectInvoice($id,$this->input->post('reason',true),$this->uid(),$this->roleId());},'Invoice ditolak dan dikembalikan ke Draft.');}
public function delete_draft($id){return$this->mutation('can_delete',function()use($id){return$this->salesservice->deleteDraftInvoice($id,$this->uid(),is_master_admin_user());},'Draft invoice dibatalkan dan seluruh reservasi dilepas.');}
public function delete_draft($id){return$this->mutation('can_delete',function()use($id){return$this->salesservice->deleteDraftInvoice($id,$this->uid(),is_master_admin_user());},'Draft invoice dibatalkan, stok/barcode tersedia kembali, dan jurnal awal telah dibalik.');}
public function pdf($id)
{
$this->requirePermission('invoices','can_export');$this->guardView($this->salesservice->invoiceForEditor($id));$data=$this->salesservice->invoiceDetail($id);$includePrevious=(int)$this->input->get('include_previous')===1;$data['previous_invoices']=$includePrevious?$this->salesservice->previousOutstandingInvoices($id):array();$this->addPdfCompanySettings($data);require_once FCPATH.'vendor/autoload.php';$html=$this->load->view('sales/invoice_pdf',$data,true);$pdf=new Dompdf\Dompdf();$pdf->loadHtml($html);$pdf->setPaper('A4','portrait');$pdf->render();$pdf->stream('Invoice-'.$data['invoice']->no_invoice.'.pdf',array('Attachment'=>false));
+7
View File
@@ -693,6 +693,13 @@ class Items extends MY_Controller {
return json_response(false, 'Barang tidak ditemukan.', array(), 404);
}
if ($item->status === 'active') {
$availability = $this->db->query("SELECT COUNT(*) barcode_count,COALESCE(SUM(CASE WHEN status IN('available','reserved') THEN GREATEST(qty_sisa-COALESCE(reserved_qty,0)-COALESCE(return_reserved_qty,0),0) ELSE 0 END),0) available_stock,COALESCE(SUM(COALESCE(reserved_qty,0)+COALESCE(return_reserved_qty,0)),0) reserved_stock FROM item_barcodes WHERE item_id=?", array((int)$item->id))->row();
$item->physical_stock = (float)$item->stok;
$item->available_stock = $availability && (int)$availability->barcode_count > 0 ? (float)$availability->available_stock : (float)$item->stok;
$item->reserved_stock = $availability ? (float)$availability->reserved_stock : 0;
}
$logs = $this->db
->select('stock_logs.*, warehouses.nama as gudang')
->join('warehouses','warehouses.id = stock_logs.warehouse_id','left')
+1 -1
View File
@@ -12,7 +12,7 @@ class Receivables extends MY_Controller
{
$focusId=(int)$this->input->get('invoice_id');$focus=null;
if($focusId)$focus=$this->db->select('i.id,i.customer_id,i.no_invoice,i.sisa_piutang,c.nama customer_name')->from('invoices i')->join('customers c','c.id=i.customer_id')->where(array('i.id'=>$focusId,'i.company_id'=>$this->company(),'i.workflow_status'=>'posted'))->where('i.sisa_piutang >',0)->get()->row();
$data=array('active_menu'=>'receivables','aging'=>$this->receivableservice->aging($this->input->get('as_of')?:date('Y-m-d')),'focus_invoice'=>$focus,'can_post'=>is_master_admin_user()||check_permission('invoices','can_post'));
$data=array('active_menu'=>'receivables','aging'=>$this->receivableservice->aging($this->input->get('as_of')?:date('Y-m-d')),'focus_invoice'=>$focus,'can_post'=>is_master_admin_user()||check_permission('invoices','can_post'),'summary'=>$this->salesservice->dashboard());
$this->load->view('partials/header',$data);$this->load->view('receivables/index',$data);$this->load->view('partials/footer',$data);
}
+28 -1
View File
@@ -57,7 +57,34 @@ class Sales extends MY_Controller
public function return_pdf($id){$data=$this->salesservice->returnDetail($id);$this->pdf('sales/return_pdf',$data,'Retur-'.$data['return']->return_no.'.pdf');}
public function receipt_pdf($id)
{
$company=(int)$this->companycontext->id();$data['payment']=$this->db->select('p.*,c.nama customer_name,c.alamat')->from('payments p')->join('customers c','c.id=p.customer_id')->where(array('p.id'=>(int)$id,'p.company_id'=>$company))->get()->row();if(!$data['payment'])show_404();$data['allocations']=$this->db->select('a.*,i.no_invoice')->from('payment_allocations a')->join('invoices i','i.id=a.invoice_id')->where(array('a.payment_id'=>(int)$id,'i.company_id'=>$company))->get()->result();$data['sources']=$this->db->select('s.*,a.kode_akun,a.nama_akun')->from('payment_sources s')->join('accounts a','a.id=s.account_id')->where(array('s.payment_id'=>(int)$id,'s.company_id'=>$company))->get()->result();$this->pdf('sales/receipt_pdf',$data,'Bukti-Penerimaan-'.$data['payment']->payment_no.'.pdf');
$company=(int)$this->companycontext->id();
$data['payment']=$this->db
->select('p.*,c.nama customer_name,c.alamat customer_address,c.telp customer_phone,c.email customer_email,u.nama creator_name')
->from('payments p')
->join('customers c','c.id=p.customer_id')
->join('users u','u.id=p.created_by','left')
->where(array('p.id'=>(int)$id,'p.company_id'=>$company))
->get()
->row();
if(!$data['payment'])show_404();
$data['allocations']=$this->db
->select('a.*,i.no_invoice,i.tanggal invoice_date,i.jatuh_tempo')
->from('payment_allocations a')
->join('invoices i','i.id=a.invoice_id')
->where(array('a.payment_id'=>(int)$id,'i.company_id'=>$company))
->order_by('i.tanggal','ASC')
->order_by('i.id','ASC')
->get()
->result();
$data['sources']=$this->db
->select('s.*,a.kode_akun,a.nama_akun')
->from('payment_sources s')
->join('accounts a','a.id=s.account_id')
->where(array('s.payment_id'=>(int)$id,'s.company_id'=>$company))
->order_by('s.id','ASC')
->get()
->result();
$this->pdf('sales/receipt_pdf',$data,'Bukti-Penerimaan-'.$data['payment']->payment_no.'.pdf');
}
private function decodeRows(){$rows=json_decode((string)$this->input->post('rows'),true);if(!is_array($rows))throw new BusinessException('Detail barang tidak valid.');return$rows;}
private function action($fn,$message){if(strtoupper($this->input->method())!=='POST')return json_response(false,'Metode tidak diizinkan.',array(),405);try{return json_response(true,$message,array('result'=>$fn()));}catch(Throwable$e){return business_exception_response($e);}}