requirePermission('employees');$this->load->database();$this->load->library('PayrollService');} private function uid(){return(int)$this->session->userdata('user_id');}private function go($ok,$m){$this->session->set_flashdata($ok?'success':'error',$m);redirect('hrpayroll');} public function index(){$d=array('active_menu'=>'hr_payroll');$d['periods']=$this->db->select('p.*,COUNT(x.id) employee_count,COALESCE(SUM(x.gross_salary),0) gross,COALESCE(SUM(x.total_deductions),0) deductions,COALESCE(SUM(x.total_salary),0) net')->from('k_payroll_periods p')->join('k_payrolls x','x.payroll_period_id=p.id','left')->group_by('p.id')->order_by('p.start_date','DESC')->get()->result();$d['components']=$this->db->order_by('code')->get('payroll_components')->result();$d['locks']=$this->db->order_by('period','DESC')->get('attendance_period_locks')->result();$d['cash_accounts']=$this->db->where('is_active',1)->get('cash_accounts')->result();$this->load->view('partials/header',$d);$this->load->view('hr_payroll/index',$d);$this->load->view('partials/footer');} public function lock_attendance(){try{$period=$this->input->post('period');if($this->db->get_where('attendance_period_locks',array('period'=>$period,'branch_id'=>null))->row())$this->db->where('period',$period)->where('branch_id IS NULL',null,false)->update('attendance_period_locks',array('status'=>'locked','locked_by'=>$this->uid(),'locked_at'=>date('Y-m-d H:i:s'),'notes'=>$this->input->post('notes',true)));else$this->db->insert('attendance_period_locks',array('period'=>$period,'status'=>'locked','locked_by'=>$this->uid(),'locked_at'=>date('Y-m-d H:i:s'),'notes'=>$this->input->post('notes',true)));$this->go(true,'Absensi '.$period.' dikunci.');}catch(Throwable$e){$this->go(false,$e->getMessage());}} public function calculate(){try{$id=$this->payrollservice->calculate($this->input->post('month'),$this->uid());$this->go(true,'Draft payroll dihitung. Periode #'.$id);}catch(Throwable$e){$this->go(false,$e->getMessage());}} public function workflow($id,$action){try{$this->payrollservice->workflow($id,$action,$this->uid());$this->go(true,'Workflow payroll berhasil: '.$action);}catch(Throwable$e){$this->go(false,$e->getMessage());}} public function post($id){try{$this->payrollservice->post($id,$this->input->post('date'),$this->uid());$this->go(true,'Payroll berhasil diposting ke jurnal.');}catch(Throwable$e){$this->go(false,$e->getMessage());}} public function pay($id){try{$this->payrollservice->pay($id,$this->input->post('date'),(int)$this->input->post('cash_account_id'),$this->uid());$this->go(true,'Payroll berhasil dibayar dan jurnal pembayaran diposting.');}catch(Throwable$e){$this->go(false,$e->getMessage());}} public function component(){try{$id=(int)$this->input->post('id');$data=array('code'=>strtoupper(trim($this->input->post('code',true))),'name'=>$this->input->post('name',true),'component_type'=>$this->input->post('component_type'),'category'=>$this->input->post('category'),'formula_expression'=>$this->input->post('formula_expression',true)?:null,'fixed_amount'=>$this->input->post('fixed_amount')!==''?(float)$this->input->post('fixed_amount'):null,'taxable'=>$this->input->post('taxable')?1:0,'prorated'=>$this->input->post('prorated')?1:0,'expense_account_id'=>$this->input->post('expense_account_id')?:null,'liability_account_id'=>$this->input->post('liability_account_id')?:null,'is_active'=>1);if($id)$this->db->where('id',$id)->update('payroll_components',$data);else$this->db->insert('payroll_components',$data);$this->go(true,'Komponen payroll disimpan.');}catch(Throwable$e){$this->go(false,$e->getMessage());}} public function slip($payrollId){$p=$this->db->select('x.*,e.full_name,e.employee_code,r.period_name,r.workflow_status')->from('k_payrolls x')->join('k_employees e','e.id=x.employee_id')->join('k_payroll_periods r','r.id=x.payroll_period_id')->where('x.id',$payrollId)->get()->row();if(!$p||!in_array($p->workflow_status,array('final','posted','paid'),true))show_error('Slip belum tersedia.',422);$lines=$this->db->where('payroll_id',$payrollId)->get('payroll_calculation_lines')->result();$this->load->view('hr_payroll/slip',array('payroll'=>$p,'lines'=>$lines));} }