diff --git a/application/controllers/Employees.php b/application/controllers/Employees.php
index e16bfbf..bbf4c87 100644
--- a/application/controllers/Employees.php
+++ b/application/controllers/Employees.php
@@ -407,75 +407,64 @@ class Employees extends MY_Controller {
// =========================================================
private function prepareData()
{
+ $nullableText = function ($value) {
+ $value = trim((string) $value);
+ return $value === '' || in_array(strtolower($value), array('undefined', 'null'), true)
+ ? null
+ : $value;
+ };
+ $nullableId = function ($value) {
+ $value = (int) $value;
+ return $value > 0 ? $value : null;
+ };
+
$data = [
- 'nik' => trim($this->input->post('nik')),
+ 'nik' => $nullableText($this->input->post('nik')),
'full_name' => trim(
$this->input->post('full_name')
),
- 'gender' => $this->input->post('gender'),
+ 'gender' => $nullableText($this->input->post('gender')),
- 'birth_place' => trim(
- $this->input->post('birth_place')
- ),
+ 'birth_place' => $nullableText($this->input->post('birth_place')),
- 'birth_date' => $this->input->post('birth_date'),
+ 'birth_date' => $nullableText($this->input->post('birth_date')),
- 'phone' => trim(
- $this->input->post('phone')
- ),
+ 'phone' => $nullableText($this->input->post('phone')),
- 'email' => trim(
- $this->input->post('email')
- ),
+ 'email' => $nullableText($this->input->post('email')),
- 'address' => trim(
- $this->input->post('address')
- ),
+ 'address' => $nullableText($this->input->post('address')),
'join_date' => $this->input->post('join_date'),
- 'resign_date' => $this->input->post('resign_date'),
+ 'resign_date' => $nullableText($this->input->post('resign_date')),
'employment_status' => $this->input->post('employment_status'),
'marital_status' => $this->input->post('marital_status'),
- 'religion' => trim(
- $this->input->post('religion')
- ),
+ 'religion' => $nullableText($this->input->post('religion')),
- 'department_id' => $this->input->post('department_id'),
+ 'department_id' => $nullableId($this->input->post('department_id')),
- 'position_id' => $this->input->post('position_id'),
+ 'position_id' => $nullableId($this->input->post('position_id')),
- 'branch_id' => $this->input->post('branch_id'),
+ 'branch_id' => $nullableId($this->input->post('branch_id')),
- 'bank_name' => trim(
- $this->input->post('bank_name')
- ),
+ 'bank_name' => $nullableText($this->input->post('bank_name')),
- 'bank_account_number' => trim(
- $this->input->post('bank_account_number')
- ),
+ 'bank_account_number' => $nullableText($this->input->post('bank_account_number')),
- 'bank_account_name' => trim(
- $this->input->post('bank_account_name')
- ),
+ 'bank_account_name' => $nullableText($this->input->post('bank_account_name')),
- 'bpjs_kesehatan_number' => trim(
- $this->input->post('bpjs_kesehatan_number')
- ),
+ 'bpjs_kesehatan_number' => $nullableText($this->input->post('bpjs_kesehatan_number')),
- 'bpjs_ketenagakerjaan_number' => trim(
- $this->input->post('bpjs_ketenagakerjaan_number')
- ),
+ 'bpjs_ketenagakerjaan_number' => $nullableText($this->input->post('bpjs_ketenagakerjaan_number')),
- 'fingerprint_user_id' => trim(
- $this->input->post('fingerprint_user_id')
- ),
+ 'fingerprint_user_id' => $nullableText($this->input->post('fingerprint_user_id')),
'is_active' => $this->input->post('is_active') === '0' ? 0 : 1
];
@@ -492,7 +481,10 @@ class Employees extends MY_Controller {
private function canEmployee($action)
{
- return is_master_admin_user() || check_permission('hr_employees',$action) || check_permission('employees',$action);
+ return $this->session->userdata('role') === 'Admin'
+ || is_master_admin_user()
+ || check_permission('hr_employees',$action)
+ || check_permission('employees',$action);
}
private function maskRestrictedSalary($employee)
diff --git a/application/controllers/Organizationmaster.php b/application/controllers/Organizationmaster.php
index 437c163..a868b14 100644
--- a/application/controllers/Organizationmaster.php
+++ b/application/controllers/Organizationmaster.php
@@ -9,5 +9,5 @@ class Organizationmaster extends MY_Controller
private function saveMaster($table,$nameField,$position=false){$id=(int)$this->input->post('id');$code=strtoupper(trim($this->input->post('code',true)));$name=trim($this->input->post('name',true));if($code===''||$name==='')return json_response(false,'Kode dan nama wajib diisi.',array(),422);$q=$this->db->where('code',$code);if($id)$q->where('id !=',$id);if($q->count_all_results($table))return json_response(false,'Kode sudah digunakan.',array(),422);$data=array('code'=>$code,$nameField=>$name,'description'=>trim($this->input->post('description',true))?:null,'is_active'=>$this->input->post('is_active')?1:0);if($position)$data['department_id']=$this->input->post('department_id')?:null;if($id)$this->db->where('id',$id)->update($table,$data);else{$this->db->insert($table,$data);$id=$this->db->insert_id();}return json_response(true,'Data berhasil disimpan.',array('id'=>$id));}
public function delete_department($id){return$this->deactivate('k_departments',$id,'department_id');}
public function delete_position($id){return$this->deactivate('k_positions',$id,'position_id');}
- private function deactivate($table,$id,$employeeField){if($this->db->where($employeeField,(int)$id)->count_all_results('k_employees')){$this->db->where('id',(int)$id)->update($table,array('is_active'=>0));return json_response(true,'Data sudah digunakan karyawan, sehingga dinonaktifkan dan tidak dihapus.');}$this->db->where('id',(int)$id)->delete($table);return json_response(true,'Data berhasil dihapus.');}
+ private function deactivate($table,$id,$employeeField){$id=(int)$id;$used=$this->db->where($employeeField,$id)->count_all_results('k_employees')>0;if($table==='k_departments'&&$this->db->where('department_id',$id)->count_all_results('k_positions')>0)$used=true;if($used){$this->db->where('id',$id)->update($table,array('is_active'=>0));return json_response(true,'Data sudah digunakan, sehingga dinonaktifkan dan tidak dihapus.');}$this->db->where('id',$id)->delete($table);return json_response(true,'Data berhasil dihapus.');}
}
diff --git a/application/libraries/PayrollService.php b/application/libraries/PayrollService.php
index a242700..3ab9296 100644
--- a/application/libraries/PayrollService.php
+++ b/application/libraries/PayrollService.php
@@ -26,7 +26,19 @@ class PayrollService
if($period->workflow_status!=='draft')throw new BusinessException('Hanya payroll Draft yang dapat dihitung ulang.');
$start=$period->attendance_start_date?:$period->start_date;$end=$period->attendance_end_date?:$period->end_date;
if(!$db->get_where('payroll_attendance_locks',array('company_id'=>$companyId,'payroll_period_id'=>$period->id,'status'=>'locked'))->row())throw new BusinessException('Kehadiran periode ini harus dikunci sebelum payroll dihitung.');
- $employees=$db->where(array('company_id'=>$companyId,'is_active'=>1))->order_by('full_name')->get('k_employees')->result();
+ $employmentEmployees=$db
+ ->where(array('company_id'=>$companyId,'is_active'=>1))
+ ->where('join_date <=',$end)
+ ->group_start()
+ ->where('resign_date IS NULL',null,false)
+ ->or_where('resign_date','0000-00-00')
+ ->or_where('resign_date >=',$start)
+ ->group_end()
+ ->order_by('full_name')
+ ->get('k_employees')
+ ->result();
+ $this->removeIneligibleDraftPayrolls($period->id,$companyId,$employmentEmployees);
+ $employees=$employmentEmployees;
if(!$canManageAll)$employees=array_values(array_filter($employees,function($employee){return empty($employee->salary_restricted);}));
if(!$employees)throw new BusinessException('Tidak ada karyawan yang dapat dihitung dengan permission Anda.');
foreach($employees as$employee)$this->calculateEmployee($period,$employee,$start,$end,$companyId,$uid);
@@ -37,6 +49,20 @@ class PayrollService
}catch(Throwable$e){$db->trans_rollback();throw$e;}
}
+ private function removeIneligibleDraftPayrolls($periodId,$companyId,$eligibleEmployees)
+ {
+ $eligibleIds=array_map(function($employee){return(int)$employee->id;},$eligibleEmployees);
+ $query=$this->CI->db
+ ->select('id')
+ ->where(array('company_id'=>(int)$companyId,'payroll_period_id'=>(int)$periodId));
+ if($eligibleIds)$query->where_not_in('employee_id',$eligibleIds);
+ $stale=$query->get('k_payrolls')->result();
+ foreach($stale as$row){
+ $this->CI->db->where('payroll_id',(int)$row->id)->delete('payroll_calculation_lines');
+ $this->CI->db->where('id',(int)$row->id)->delete('k_payrolls');
+ }
+ }
+
private function calculateEmployee($period,$employee,$start,$end,$companyId,$uid)
{
$db=$this->CI->db;
diff --git a/application/migrations/20260914000100_normalize_employee_resign_dates.php b/application/migrations/20260914000100_normalize_employee_resign_dates.php
new file mode 100644
index 0000000..d34379b
--- /dev/null
+++ b/application/migrations/20260914000100_normalize_employee_resign_dates.php
@@ -0,0 +1,22 @@
+db->table_exists('k_employees') || !$this->db->field_exists('resign_date', 'k_employees')) {
+ return;
+ }
+
+ // Database lama memakai zero-date sebagai nilai kosong. NULL adalah
+ // representasi yang valid untuk karyawan yang belum/tidak resign.
+ $this->db->query("UPDATE k_employees SET resign_date = NULL WHERE CAST(resign_date AS CHAR) = '0000-00-00'");
+ }
+
+ public function down()
+ {
+ // Tidak dikembalikan ke zero-date karena nilai tersebut tidak valid
+ // pada MySQL strict mode.
+ }
+}
diff --git a/application/views/employees/attendance_monitoring.php b/application/views/employees/attendance_monitoring.php
index bfe2217..e26ca3d 100644
--- a/application/views/employees/attendance_monitoring.php
+++ b/application/views/employees/attendance_monitoring.php
@@ -20,7 +20,7 @@
-
DRAFT ABSENSI
Tambah Absen Manual
+
DRAFT ABSENSI
Tambah Absen Manual
diff --git a/application/views/hr_payroll/organization_master.php b/application/views/hr_payroll/organization_master.php
index 2d26b95..36726d1 100644
--- a/application/views/hr_payroll/organization_master.php
+++ b/application/views/hr_payroll/organization_master.php
@@ -2,11 +2,11 @@
load->view('hr_payroll/_header',array('active_menu'=>'organization_master','title'=>'Departemen & Posisi','subtitle'=>'Satu sumber data organisasi untuk karyawan, pembelian, payroll, jadwal, dan modul terkait.')); ?>