Files
accounting_dev_v2/application/controllers/Salesdocuments.php
T
2026-09-11 16:03:00 +07:00

15 lines
7.2 KiB
PHP

<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Salesdocuments extends MY_Controller
{
public function __construct(){parent::__construct();$this->load->library(array('NumberingService','AuditService','TransactionService'));}
private function company(){return(int)$this->companycontext->id();}private function uid(){return(int)$this->session->userdata('user_id');}
public function index(){$data=array('active_menu'=>'sales_documents','rows'=>$this->db->select('s.*,c.nama')->from('sales_documents s')->join('customers c','c.id=s.customer_id')->where('s.company_id',$this->company())->order_by('s.document_date','DESC')->limit(200)->get()->result(),'permissions'=>array('create'=>is_master_admin_user()||check_permission('invoices','can_create'),'update'=>is_master_admin_user()||check_permission('invoices','can_update'),'approve'=>is_master_admin_user()||check_permission('invoices','can_approve'),'export'=>is_master_admin_user()||check_permission('invoices','can_export')));$this->load->view('partials/header',$data);$this->load->view('sales_documents/index',$data);$this->load->view('partials/footer',$data);}
public function detail($id){try{$d=$this->db->select('s.*,c.nama customer_name,c.alamat')->from('sales_documents s')->join('customers c','c.id=s.customer_id')->where(array('s.id'=>(int)$id,'s.company_id'=>$this->company()))->get()->row();if(!$d)throw new BusinessException('Dokumen penjualan tidak ditemukan.');$lines=$this->db->select('l.*,i.kode_detail AS kode_barang,i.nama_barang')->from('sales_document_lines l')->join('items i','i.id=l.item_id','left')->where(array('l.sales_document_id'=>$d->id,'l.company_id'=>$this->company()))->order_by('l.id')->get()->result();return json_response(true,'Detail ditemukan.',array('document'=>$d,'lines'=>$lines));}catch(Throwable$e){return business_exception_response($e);}}
public function save(){return$this->action(function(){$type=$this->input->post('document_type');if(!in_array($type,array('quotation','sales_order'),true))throw new BusinessException('Jenis dokumen tidak valid.');$customer=$this->db->get_where('customers',array('id'=>(int)$this->input->post('customer_id'),'company_id'=>$this->company()))->row();if(!$customer)throw new BusinessException('Customer tidak valid.');$rows=json_decode((string)$this->input->post('rows'),true);if(!is_array($rows)||!$rows)throw new BusinessException('Tambahkan minimal satu barang.');$date=$this->input->post('document_date');$subtotal=0;$taxTotal=0;$clean=array();foreach($rows as$n=>$row){$item=$this->db->get_where('items',array('id'=>(int)($row['item_id']??0),'company_id'=>$this->company(),'status'=>'active'))->row();$qty=round((float)str_replace(',','.',(string)($row['qty']??0)),4);$price=$this->money($row['unit_price']??0);$discount=max(0,$this->money($row['discount_amount']??0));$tax=max(0,(float)str_replace(',','.',(string)($row['tax_rate']??0)));if(!$item||$qty<=0||$discount>$qty*$price)throw new BusinessException('Detail barang baris '.($n+1).' tidak valid.');$base=round($qty*$price-$discount,2);$taxAmount=round($base*$tax/100,2);$subtotal+=$base;$taxTotal+=$taxAmount;$clean[]=array('item'=>$item,'qty'=>$qty,'price'=>$price,'discount'=>$discount,'tax'=>$tax,'tax_amount'=>$taxAmount,'total'=>$base+$taxAmount,'specification'=>trim((string)($row['specification']??'')));}$no=$this->numberingservice->next($type,$date);$this->db->insert('sales_documents',array('company_id'=>$this->company(),'document_type'=>$type,'document_no'=>$no,'customer_id'=>$customer->id,'document_date'=>$date,'valid_until'=>$this->input->post('valid_until')?:null,'status'=>'draft','subtotal'=>$subtotal,'tax_total'=>$taxTotal,'total'=>$subtotal+$taxTotal,'notes'=>$this->input->post('notes',true),'created_by'=>$this->uid(),'created_at'=>date('Y-m-d H:i:s')));$id=(int)$this->db->insert_id();foreach($clean as$x)$this->db->insert('sales_document_lines',array('company_id'=>$this->company(),'sales_document_id'=>$id,'item_id'=>$x['item']->id,'account_id'=>null,'description'=>$x['item']->nama_barang,'specification'=>$x['specification'],'unit'=>null,'qty'=>$x['qty'],'unit_price'=>$x['price'],'discount_amount'=>$x['discount'],'tax_rate'=>$x['tax'],'tax_amount'=>$x['tax_amount'],'line_total'=>$x['total']));$this->auditservice->record('sales','sales_document',$id,'create',null,array('document_no'=>$no,'line_count'=>count($clean),'total'=>$subtotal+$taxTotal),$this->uid());return$id;},'Dokumen penjualan berhasil disimpan.');}
public function workflow($id,$state){return$this->action(function()use($id,$state){$d=$this->db->query('SELECT * FROM sales_documents WHERE id=? AND company_id=? FOR UPDATE',array((int)$id,$this->company()))->row();if(!$d)throw new BusinessException('Dokumen tidak ditemukan.');$allowed=array('draft'=>array('sent','cancelled'),'sent'=>array('accepted','rejected'),'accepted'=>array('cancelled'));if(!in_array($state,$allowed[$d->status]??array(),true))throw new BusinessException('Perubahan status dokumen tidak diizinkan.');$this->db->where('id',$d->id)->update('sales_documents',array('status'=>$state,'updated_by'=>$this->uid(),'updated_at'=>date('Y-m-d H:i:s')));$this->auditservice->record('sales','sales_document',$d->id,'workflow',$d,array('status'=>$state),$this->uid());return$d->id;},'Status dokumen berhasil diperbarui.');}
public function convert($id){return$this->action(function()use($id){$d=$this->db->query('SELECT * FROM sales_documents WHERE id=? AND company_id=? FOR UPDATE',array((int)$id,$this->company()))->row();if(!$d||$d->document_type!=='quotation'||$d->status!=='accepted')throw new BusinessException('Hanya Penawaran Diterima yang dapat dijadikan Sales Order.');$existing=$this->db->get_where('sales_documents',array('source_quotation_id'=>$d->id,'company_id'=>$this->company()))->row();if($existing)return$existing->id;$no=$this->numberingservice->next('sales_order',date('Y-m-d'));$copy=(array)$d;unset($copy['id']);$copy['document_type']='sales_order';$copy['document_no']=$no;$copy['document_date']=date('Y-m-d');$copy['status']='draft';$copy['source_quotation_id']=$d->id;$copy['parent_document_id']=$d->id;$copy['created_by']=$this->uid();$copy['created_at']=date('Y-m-d H:i:s');$copy['updated_at']=null;$copy['updated_by']=null;$this->db->insert('sales_documents',$copy);$new=(int)$this->db->insert_id();foreach($this->db->get_where('sales_document_lines',array('sales_document_id'=>$d->id,'company_id'=>$this->company()))->result_array()as$l){unset($l['id']);$l['sales_document_id']=$new;$this->db->insert('sales_document_lines',$l);}$this->db->where('id',$d->id)->update('sales_documents',array('status'=>'converted','updated_at'=>date('Y-m-d H:i:s'),'updated_by'=>$this->uid()));return$new;},'Penawaran berhasil dikonversi menjadi Sales Order.');}
private function money($v){$v=trim(str_ireplace(array('Rp',' '),'',(string)$v));if(strpos($v,',')!==false)$v=str_replace(',','.',str_replace('.','',$v));elseif(preg_match('/^\d{1,3}(\.\d{3})+$/',$v))$v=str_replace('.','',$v);return round((float)$v,2);}
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'=>$this->transactionservice->run($fn)));}catch(Throwable$e){return business_exception_response($e);}}
}