Files
accounting_dev_v2/application/controllers/Payrollcheck.php
T
2026-09-14 11:14:25 +07:00

51 lines
8.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/** Pemeriksaan CLI terarah. Seluruh fixture selalu di-rollback. */
class Payrollcheck extends CI_Controller
{
public function exports()
{
if(!$this->input->is_cli_request())show_404();
$this->load->database();if(is_file(FCPATH.'vendor/autoload.php'))require_once FCPATH.'vendor/autoload.php';
$period=$this->db->order_by('id')->get('k_payroll_periods')->row();if(!$period){echo"SKIP: periode payroll belum tersedia.\n";return;}
$company=$this->db->get_where('companies',array('id'=>$period->company_id))->row();$rows=$this->db->select('x.*,e.employee_code,e.full_name,e.bank_name,e.bank_account_number')->from('k_payrolls x')->join('k_employees e','e.id=x.employee_id')->where('x.payroll_period_id',$period->id)->limit(3)->get()->result();
$html=$this->load->view('hr_payroll/register_pdf',array('period'=>$period,'rows'=>$rows,'company'=>$company),true);$pdf=new Dompdf\Dompdf();$pdf->loadHtml($html);$pdf->setPaper('A4','landscape');$pdf->render();if(strlen($pdf->output())<500)throw new RuntimeException('Render PDF payroll gagal.');
$book=new PhpOffice\PhpSpreadsheet\Spreadsheet();$book->getActiveSheet()->fromArray(array(array('Kode','Nama','Neto'),array($rows?$rows[0]->employee_code:'-',$rows?$rows[0]->full_name:'-', $rows?(float)$rows[0]->total_salary:0)),null,'A1');$file=tempnam(sys_get_temp_dir(),'payroll-check-').'.xlsx';(new PhpOffice\PhpSpreadsheet\Writer\Xlsx($book))->save($file);if(!is_file($file)||filesize($file)<1000)throw new RuntimeException('Render XLSX payroll gagal.');unlink($file);echo"PASS: satu PDF register dan satu XLSX payroll berhasil dirender; file uji dihapus.\n";
}
public function index()
{
if(!$this->input->is_cli_request())show_404();
$this->load->database();$this->load->library('PayrollService');
$company=$this->db->order_by('id')->get('companies')->row();
if(!$company){echo"SKIP: perusahaan tidak tersedia.\n";return;}
$employee=$this->db->where(array('company_id'=>$company->id,'is_active'=>1,'salary_restricted'=>0))->order_by('id')->get('k_employees')->row();
$user=$this->db->order_by('id')->get('users')->row();
if(!$employee||!$user){echo"SKIP: karyawan biasa atau user tidak tersedia.\n";return;}
$access=$this->db->get_where('user_companies',array('company_id'=>$company->id))->row();if(!$access){echo"SKIP: akses user perusahaan tidak tersedia.\n";return;}$user=$this->db->get_where('users',array('id'=>$access->user_id))->row();$this->session->set_userdata(array('company_id'=>(int)$company->id,'user_id'=>(int)$user->id));
$this->db->trans_begin(true);
try{
$start='2098-08-14';$end='2098-09-13';$now=date('Y-m-d H:i:s');
$this->db->insert('k_payroll_periods',array('company_id'=>$company->id,'period_name'=>'[TEST ROLLBACK] Flexible payroll','start_date'=>$start,'end_date'=>$end,'attendance_start_date'=>$start,'attendance_end_date'=>$end,'payment_date'=>'2098-09-25','status'=>'draft','workflow_status'=>'draft','generated_at'=>$now));$periodId=(int)$this->db->insert_id();
$componentIds=array();foreach(array(array('SMOKE_EARN','[TEST] Pendapatan','earning'),array('SMOKE_DED','[TEST] Potongan Tetap','deduction'),array('SMOKE_ALPHA','[TEST] Potongan Alpha','deduction'),array('SMOKE_BONUS','[TEST] Bonus Periode','earning'))as$component){$this->db->insert('payroll_components',array('company_id'=>$company->id,'code'=>$component[0].'-'.bin2hex(random_bytes(2)),'name'=>$component[1],'component_type'=>$component[2],'category'=>'other','calculation_method'=>'fixed','default_value'=>0,'fixed_amount'=>0,'taxable'=>0,'prorated'=>0,'is_active'=>1,'effective_from'=>$start,'created_at'=>$now));$componentIds[]=(int)$this->db->insert_id();}
foreach(array(array($componentIds[0],100),array($componentIds[1],10))as$assignment)$this->db->insert('employee_payroll_components',array('company_id'=>$company->id,'employee_id'=>$employee->id,'component_id'=>$assignment[0],'calculation_method'=>'fixed','amount_override'=>$assignment[1],'effective_from'=>$start,'is_active'=>1,'notes'=>'[TEST ROLLBACK]','created_at'=>$now));
$this->db->insert('payroll_attendance_rules',array('company_id'=>$company->id,'component_id'=>$componentIds[2],'rule_name'=>'[TEST] Alpha per hari','condition_type'=>'alpha','calculation_method'=>'per_day','rate_value'=>25,'effective_from'=>$start,'is_active'=>1,'created_at'=>$now));
$this->db->insert('payroll_period_adjustments',array('company_id'=>$company->id,'payroll_period_id'=>$periodId,'employee_id'=>$employee->id,'component_id'=>$componentIds[3],'adjustment_type'=>'earning','amount'=>50,'notes'=>'[TEST ROLLBACK] Bonus','is_active'=>1,'created_by'=>$user->id,'created_at'=>$now));
$this->db->insert('k_attendances',array('employee_id'=>$employee->id,'attendance_date'=>'2098-09-01','attendance_status'=>'alpha','late_minutes'=>0,'early_leave_minutes'=>0));
$this->db->insert('k_attendances',array('employee_id'=>$employee->id,'attendance_date'=>'2098-09-02','attendance_status'=>'late','late_minutes'=>30,'early_leave_minutes'=>0));
$this->db->insert('payroll_attendance_locks',array('company_id'=>$company->id,'payroll_period_id'=>$periodId,'start_date'=>$start,'end_date'=>$end,'status'=>'locked','locked_by'=>$user->id,'locked_at'=>$now));
$this->payrollservice->calculate($periodId,$user->id,true,$company->id);$first=$this->db->select('COUNT(*) count')->from('payroll_calculation_lines l')->join('k_payrolls p','p.id=l.payroll_id')->where('p.payroll_period_id',$periodId)->get()->row();
$this->payrollservice->calculate($periodId,$user->id,true,$company->id);$second=$this->db->select('COUNT(*) count')->from('payroll_calculation_lines l')->join('k_payrolls p','p.id=l.payroll_id')->where('p.payroll_period_id',$periodId)->get()->row();
$codes=$this->db->select('c.name,l.amount')->from('payroll_calculation_lines l')->join('k_payrolls p','p.id=l.payroll_id')->join('payroll_components c','c.id=l.component_id')->where('p.payroll_period_id',$periodId)->where_in('c.id',$componentIds)->get()->result();$found=array();foreach($codes as$row)$found[$row->name]=(float)$row->amount;
if((int)$first->count!==(int)$second->count)throw new RuntimeException('Recalculate menggandakan atau menghilangkan baris.');
if(($found['[TEST] Pendapatan']??0)!==100.0||($found['[TEST] Potongan Tetap']??0)!==10.0||($found['[TEST] Potongan Alpha']??0)!==25.0||($found['[TEST] Bonus Periode']??0)!==50.0)throw new RuntimeException('Pendapatan, potongan, aturan alpha, atau bonus tidak sesuai.');
$snapshot=$this->db->select('calculation_snapshot')->get_where('k_payrolls',array('payroll_period_id'=>$periodId,'employee_id'=>$employee->id))->row();if(!$snapshot||strpos($snapshot->calculation_snapshot,$start)===false||strpos($snapshot->calculation_snapshot,$end)===false)throw new RuntimeException('Snapshot rentang fleksibel tidak tersimpan.');
$late=$this->db->where(array('company_id'=>$company->id,'condition_type'=>'late','calculation_method'=>'none'))->count_all_results('payroll_attendance_rules');if(!$late)throw new RuntimeException('Default terlambat tanpa potongan tidak tersedia.');
$beforeJournal=(int)$this->db->count_all('journals');$this->db->where('id',$periodId)->update('k_payroll_periods',array('workflow_status'=>'final'));if((int)$this->db->count_all('journals')!==$beforeJournal)throw new RuntimeException('Finalisasi membuat jurnal sebelum Posting.');$journalId=$this->payrollservice->post($periodId,'2026-09-13',$user->id,$company->id,true);$balance=$this->db->select('SUM(debit-kredit) difference',false)->get_where('journal_details',array('journal_id'=>$journalId))->row();if(!$balance||abs((float)$balance->difference)>.01)throw new RuntimeException('Jurnal payroll tidak balance.');$doubleBlocked=false;try{$this->payrollservice->post($periodId,'2026-09-13',$user->id,$company->id,true);}catch(Throwable$expected){$doubleBlocked=true;}if(!$doubleBlocked)throw new RuntimeException('Double posting tidak diblokir.');
echo"PASS: periode 14 Agustus–13 September, assignment, alpha, bonus, snapshot, recalculate, jurnal balance, dan idempotency posting.\n";
}catch(Throwable$e){$this->db->trans_rollback();fwrite(STDERR,'FAIL: '.$e->getMessage().PHP_EOL);exit(1);}
$this->db->trans_rollback();echo"ROLLBACK: seluruh fixture payroll dibatalkan.\n";
}
}