101 lines
13 KiB
PHP
101 lines
13 KiB
PHP
<?php
|
|
defined('BASEPATH') OR exit('No direct script access allowed');
|
|
|
|
class Sales extends MY_Controller
|
|
{
|
|
public function __construct(){parent::__construct();$this->load->library('SalesService');}
|
|
private function uid(){return(int)$this->session->userdata('user_id');}
|
|
private function roleId(){return(int)$this->session->userdata('role_id');}
|
|
private function allowed($action){return is_master_admin_user()||check_permission('invoices',$action);}
|
|
public function index()
|
|
{
|
|
$sourceOrder=(int)$this->input->get('source_order');if($sourceOrder)redirect('invoices/create?source_order='.$sourceOrder);
|
|
$this->renderWorkspace('summary');
|
|
}
|
|
public function deliveries(){return $this->renderWorkspace('delivery');}
|
|
public function returns(){return $this->renderWorkspace('return');}
|
|
private function renderWorkspace($section)
|
|
{
|
|
$active=$section==='delivery'?'sales_delivery':($section==='return'?'sales_return':'sales');
|
|
$data=array('active_menu'=>$active,'section'=>$section,'summary'=>$this->salesservice->dashboard(),'deliveries'=>$this->salesservice->deliveries(),'returns'=>$this->salesservice->returns(),'accounts'=>array(),'current_user'=>$this->uid(),'source_order'=>0,'focus'=>(int)$this->input->get('focus'),'permissions'=>array('create'=>$this->allowed('can_create'),'update'=>$this->allowed('can_update'),'approve'=>$this->allowed('can_approve'),'post'=>$this->allowed('can_post'),'delete'=>$this->allowed('can_delete'),'export'=>$this->allowed('can_export')));
|
|
$this->load->view('partials/header',$data);$this->load->view('sales/index',$data);$this->load->view('partials/footer',$data);
|
|
}
|
|
public function lookup_customers(){return$this->lookup('customers','nama',array('id','nama','alamat'));}
|
|
public function lookup_items()
|
|
{
|
|
$q=trim((string)$this->input->get('q'));$company=(int)$this->companycontext->id();$this->db->select('i.id,i.kode_detail AS kode_barang,i.nama_barang,i.harga_jual,i.harga_beli,i.kode_id,k.tracking_type,k.unit')->from('items i')->join('kode_barang k','k.id=i.kode_id','left')->where(array('i.company_id'=>$company,'i.status'=>'active'))->group_start()->like('i.kode_detail',$q)->or_like('i.nama_barang',$q)->group_end()->order_by('i.nama_barang')->limit(30);return json_response(true,'Barang ditemukan.',array('rows'=>$this->db->get()->result()));
|
|
}
|
|
public function lookup_barcodes()
|
|
{
|
|
$item=(int)$this->input->get('item_id');$warehouse=(int)$this->input->get('warehouse_id');$company=(int)$this->companycontext->id();$rows=$this->db->select('b.id,b.barcode,b.serial_number,b.qty_sisa,b.reserved_qty,b.return_reserved_qty')->from('item_barcodes b')->join('items i','i.id=b.item_id')->where(array('i.company_id'=>$company,'b.item_id'=>$item,'b.warehouse_id'=>$warehouse,'b.status'=>'available'))->where('(b.qty_sisa-b.reserved_qty-COALESCE(b.return_reserved_qty,0)) >',0,false)->order_by('b.id')->limit(100)->get()->result();return json_response(true,'Barcode tersedia.',array('rows'=>$rows));
|
|
}
|
|
private function lookup($table,$label,array$fields){$q=trim((string)$this->input->get('q'));$this->db->select(implode(',',$fields))->where('company_id',(int)$this->companycontext->id())->like($label,$q)->order_by($label)->limit(30);if($this->db->field_exists('is_active',$table))$this->db->where('is_active',1);return json_response(true,'Data ditemukan.',array('rows'=>$this->db->get($table)->result()));}
|
|
public function warehouses(){return json_response(true,'Gudang ditemukan.',array('rows'=>$this->db->select('id,nama')->where('company_id',(int)$this->companycontext->id())->order_by('nama')->get('warehouses')->result()));}
|
|
public function create_invoice(){return$this->action(function(){return$this->salesservice->createInvoice($this->input->post(),$this->decodeRows(),$this->uid());},'Invoice berhasil disimpan sebagai Draft/Berjalan.');}
|
|
public function append_invoice($id){return$this->action(function()use($id){return$this->salesservice->appendInvoiceLines($id,$this->decodeRows(),$this->uid());},'Item berhasil ditambahkan ke invoice berjalan.');}
|
|
public function submit_invoice($id){return$this->action(function()use($id){return$this->salesservice->submitInvoice($id,$this->uid());},'Invoice diajukan untuk approval.');}
|
|
public function approve_invoice($id){return$this->action(function()use($id){return$this->salesservice->approveAndPostInvoice($id,$this->uid(),$this->roleId());},'Invoice disetujui dan otomatis diposting.');}
|
|
public function reject_invoice($id){return$this->action(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_invoice($id){return$this->action(function()use($id){return$this->salesservice->deleteDraftInvoice($id,$this->uid(),is_master_admin_user());},'Draft invoice dibatalkan dan reservasi barcode dilepas.');}
|
|
public function post_delivery($id){return$this->action(function()use($id){return$this->salesservice->postDelivery($id,$this->uid());},'Surat Jalan diposting dan stok telah diperbarui.');}
|
|
public function update_delivery($id){$saved=array();try{$saved=$this->uploads('attachments',false);$result=$this->salesservice->updateDelivery($id,$this->input->post(),$saved,$this->uid());return json_response(true,'Data dan bukti pengiriman berhasil disimpan.',array('result'=>$result));}catch(Throwable$e){$this->cleanup($saved);return business_exception_response($e);}}
|
|
public function invoice_detail($id){return$this->jsonDetail(function()use($id){$this->guardInvoice($this->salesservice->invoiceForEditor($id));return$this->salesservice->invoiceDetail($id);});}
|
|
public function delivery_detail($id){return$this->jsonDetail(function()use($id){return$this->salesservice->deliveryDetail($id);});}
|
|
public function return_detail($id){return$this->jsonDetail(function()use($id){return$this->salesservice->returnDetail($id);});}
|
|
public function payment()
|
|
{
|
|
return json_response(false,'Alur pembayaran lama dinonaktifkan. Gunakan menu Piutang & Pembayaran.',array('redirect'=>base_url('receivables')),410);
|
|
}
|
|
public function create_return(){$saved=array();try{if(strtoupper($this->input->method())!=='POST')return json_response(false,'Metode tidak diizinkan.',array(),405);$saved=$this->uploads('attachments',true);$id=$this->salesservice->createReturn($this->input->post(),$this->decodeRows(),$this->uid(),$saved);return json_response(true,'Pengajuan retur berhasil disimpan.',array('result'=>$id));}catch(Throwable$e){$this->cleanup($saved);return business_exception_response($e);}}
|
|
public function submit_return($id){return$this->action(function()use($id){return$this->salesservice->submitReturn($id,$this->uid());},'Retur diajukan.');}
|
|
public function approve_return($id){return$this->action(function()use($id){return$this->salesservice->approveReturn($id,$this->uid());},'Retur disetujui.');}
|
|
public function reject_return($id){return$this->action(function()use($id){return$this->salesservice->rejectReturn($id,$this->input->post('reason',true),$this->uid());},'Retur ditolak.');}
|
|
public function receive_return($id){return$this->action(function()use($id){return$this->salesservice->receiveReturn($id,$this->uid());},'Barang retur diterima dan diperiksa.');}
|
|
public function post_return($id){return$this->action(function()use($id){return$this->salesservice->postReturn($id,$this->uid());},'Retur, stok, dan credit note berhasil diposting.');}
|
|
public function invoice_pdf($id){$this->guardInvoice($this->salesservice->invoiceForEditor($id));$data=$this->salesservice->invoiceDetail($id);$data['previous_invoices']=(int)$this->input->get('include_previous')===1?$this->salesservice->previousOutstandingInvoices($id):array();$this->pdf('sales/invoice_pdf',$data,'Invoice-'.$data['invoice']->no_invoice.'.pdf');}
|
|
public function delivery_pdf($id){$data=$this->salesservice->deliveryDetail($id);$this->pdf('sales/delivery_pdf',$data,'Surat-Jalan-'.$data['delivery']->delivery_no.'.pdf');}
|
|
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 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);}}
|
|
private function jsonDetail($fn){try{return json_response(true,'Detail ditemukan.',$fn());}catch(Throwable$e){return business_exception_response($e);}}
|
|
private function pdf($view,$data,$name){$company=(int)$this->companycontext->id();$data['pdf_company']=$this->db->get_where('companies',array('id'=>$company))->row();$currency=!empty($data['invoice']->transaction_currency)?$data['invoice']->transaction_currency:($data['pdf_company']->base_currency??'IDR');$data['pdf_currency']=$this->db->get_where('currencies',array('code'=>$currency))->row();$data['pdf_bank_accounts']=$this->db->table_exists('company_bank_accounts')?$this->db->where(array('company_id'=>$company,'is_active'=>1))->order_by('is_primary','DESC')->order_by('id')->get('company_bank_accounts')->result():array();$data['pdf_logo_data']='';$path=$data['pdf_company']->logo_path??'';if($path!==''&&is_file(FCPATH.$path)){$mime=function_exists('mime_content_type')?mime_content_type(FCPATH.$path):'image/png';$data['pdf_logo_data']='data:'.$mime.';base64,'.base64_encode(file_get_contents(FCPATH.$path));}require_once FCPATH.'vendor/autoload.php';$html=$this->load->view($view,$data,true);$pdf=new Dompdf\Dompdf();$pdf->loadHtml($html);$pdf->setPaper('A4','portrait');$pdf->render();$pdf->stream($name,array('Attachment'=>false));}
|
|
private function uploads($field,$required=false)
|
|
{
|
|
if(empty($_FILES[$field])||empty($_FILES[$field]['name'])){if($required)throw new BusinessException('Bukti pembayaran gambar atau PDF wajib diunggah.');return array();}$files=$_FILES[$field];$names=is_array($files['name'])?$files['name']:array($files['name']);$saved=array();if(count($names)>10)throw new BusinessException('Maksimal 10 file.');$dir=FCPATH.'uploads/accounting/sales/';if(!is_dir($dir)&&!mkdir($dir,0755,true))throw new BusinessException('Folder lampiran tidak dapat dibuat.');$finfo=new finfo(FILEINFO_MIME_TYPE);$allowed=array('image/jpeg'=>'jpg','image/png'=>'png','image/webp'=>'webp','image/gif'=>'gif','application/pdf'=>'pdf');
|
|
foreach($names as$i=>$name){$tmp=is_array($files['tmp_name'])?$files['tmp_name'][$i]:$files['tmp_name'];$size=is_array($files['size'])?$files['size'][$i]:$files['size'];$error=is_array($files['error'])?$files['error'][$i]:$files['error'];if($error!==UPLOAD_ERR_OK)throw new BusinessException('Salah satu bukti gagal diunggah.');$mime=$finfo->file($tmp);if(!isset($allowed[$mime]))throw new BusinessException('Lampiran hanya menerima JPG, PNG, WEBP, GIF, atau PDF.');if($size>5*1024*1024)throw new BusinessException('Ukuran setiap lampiran maksimal 5 MB.');$stored=bin2hex(random_bytes(16)).'.'.$allowed[$mime];if(!move_uploaded_file($tmp,$dir.$stored))throw new BusinessException('Lampiran tidak dapat disimpan.');$saved[]=array('original_name'=>basename($name),'stored_name'=>'uploads/accounting/sales/'.$stored,'mime_type'=>$mime,'file_size'=>$size,'sha256'=>hash_file('sha256',$dir.$stored));}return$saved;
|
|
}
|
|
private function cleanup(array$files){foreach($files as$f){$path=FCPATH.$f['stored_name'];if(is_file($path))@unlink($path);}}
|
|
private function guardInvoice($invoice){if($invoice->workflow_status==='draft'&&!is_master_admin_user()&&(int)$invoice->created_by!==$this->uid())throw new BusinessException('Draft invoice hanya dapat dilihat oleh pembuatnya.');}
|
|
}
|