Commit 9bd4d36c authored by Mahmoud Aglan's avatar Mahmoud Aglan

fixedHR

parent f6048765
...@@ -112,7 +112,7 @@ class AttendanceController extends Controller ...@@ -112,7 +112,7 @@ class AttendanceController extends Controller
'check_in_time' => $checkIn ?: null, 'check_in_time' => $checkIn ?: null,
'check_out_time' => $checkOut ?: null, 'check_out_time' => $checkOut ?: null,
'actual_hours' => (string) $actualHours, 'actual_hours' => (string) $actualHours,
'check_method' => 'manual', 'check_in_method' => 'manual',
'status' => $checkIn !== '' ? 'present' : 'absent', 'status' => $checkIn !== '' ? 'present' : 'absent',
'updated_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
'updated_by' => $employee ? (int) $employee->id : null, 'updated_by' => $employee ? (int) $employee->id : null,
......
...@@ -82,15 +82,15 @@ class ContractController extends Controller ...@@ -82,15 +82,15 @@ class ContractController extends Controller
$db = App::getInstance()->db(); $db = App::getInstance()->db();
// Previous contract in renewal chain // Previous contract in renewal chain
$previousContract = $contract->previous_contract_id $previousContract = $contract->renewal_of_contract_id
? HrContract::find((int) $contract->previous_contract_id) ? HrContract::find((int) $contract->renewal_of_contract_id)
: null; : null;
// Renewal history // Renewal history
$renewals = $db->select( $renewals = $db->select(
"SELECT id, contract_number, start_date, end_date, status "SELECT id, contract_number, start_date, end_date, status
FROM hr_contracts FROM hr_contracts
WHERE previous_contract_id = ? AND is_archived = 0 WHERE renewal_of_contract_id = ? AND is_archived = 0
ORDER BY start_date ASC", ORDER BY start_date ASC",
[(int) $id] [(int) $id]
); );
...@@ -182,7 +182,10 @@ class ContractController extends Controller ...@@ -182,7 +182,10 @@ class ContractController extends Controller
return $this->redirect('/hr/contracts/' . $id)->withError('تاريخ بداية العقد الجديد مطلوب'); return $this->redirect('/hr/contracts/' . $id)->withError('تاريخ بداية العقد الجديد مطلوب');
} }
$result = ContractService::renew((int) $id, $newStartDate, $newEndDate); $result = ContractService::renew((int) $id, [
'start_date' => $newStartDate,
'end_date' => $newEndDate,
]);
if (!$result['success']) { if (!$result['success']) {
return $this->redirect('/hr/contracts/' . $id)->withError($result['error']); return $this->redirect('/hr/contracts/' . $id)->withError($result['error']);
...@@ -198,14 +201,13 @@ class ContractController extends Controller ...@@ -198,14 +201,13 @@ class ContractController extends Controller
return $this->redirect('/hr/contracts')->withError('العقد غير موجود'); return $this->redirect('/hr/contracts')->withError('العقد غير موجود');
} }
$terminationDate = trim((string) $request->post('termination_date', date('Y-m-d')));
$terminationReason = trim((string) $request->post('termination_reason', '')); $terminationReason = trim((string) $request->post('termination_reason', ''));
if ($terminationReason === '') { if ($terminationReason === '') {
return $this->redirect('/hr/contracts/' . $id)->withError('سبب إنهاء العقد مطلوب'); return $this->redirect('/hr/contracts/' . $id)->withError('سبب إنهاء العقد مطلوب');
} }
$result = ContractService::terminate((int) $id, $terminationDate, $terminationReason); $result = ContractService::terminate((int) $id, $terminationReason);
if (!$result['success']) { if (!$result['success']) {
return $this->redirect('/hr/contracts/' . $id)->withError($result['error']); return $this->redirect('/hr/contracts/' . $id)->withError($result['error']);
......
...@@ -23,7 +23,7 @@ class DepartmentController extends Controller ...@@ -23,7 +23,7 @@ class DepartmentController extends Controller
$result = HrDepartment::search($filters, 25, $page); $result = HrDepartment::search($filters, 25, $page);
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$branches = $db->select("SELECT id, name_ar FROM branches WHERE is_archived = 0 ORDER BY name_ar"); $branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
return $this->view('HR.Views.departments.index', [ return $this->view('HR.Views.departments.index', [
'departments' => $result['data'], 'departments' => $result['data'],
...@@ -37,7 +37,7 @@ class DepartmentController extends Controller ...@@ -37,7 +37,7 @@ class DepartmentController extends Controller
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$departments = HrDepartment::allActive(); $departments = HrDepartment::allActive();
$branches = $db->select("SELECT id, name_ar FROM branches WHERE is_archived = 0 ORDER BY name_ar"); $branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
$employees = HrEmployeeProfile::getActiveIds(); $employees = HrEmployeeProfile::getActiveIds();
return $this->view('HR.Views.departments.form', [ return $this->view('HR.Views.departments.form', [
...@@ -73,8 +73,8 @@ class DepartmentController extends Controller ...@@ -73,8 +73,8 @@ class DepartmentController extends Controller
$parent = $department->parent_id ? HrDepartment::find((int) $department->parent_id) : null; $parent = $department->parent_id ? HrDepartment::find((int) $department->parent_id) : null;
$manager = $department->manager_id $manager = $department->manager_employee_id
? $db->selectOne("SELECT hp.id, hp.first_name_ar, hp.last_name_ar FROM hr_employee_profiles hp WHERE hp.id = ? AND hp.is_archived = 0", [(int) $department->manager_id]) ? $db->selectOne("SELECT hp.id, hp.first_name_ar, hp.last_name_ar FROM hr_employee_profiles hp WHERE hp.employee_id = ? AND hp.is_archived = 0", [(int) $department->manager_employee_id])
: null; : null;
$branch = $department->branch_id $branch = $department->branch_id
...@@ -82,7 +82,7 @@ class DepartmentController extends Controller ...@@ -82,7 +82,7 @@ class DepartmentController extends Controller
: null; : null;
$children = $db->select( $children = $db->select(
"SELECT id, name_ar, code FROM hr_departments WHERE parent_id = ? AND is_archived = 0 ORDER BY name_ar", "SELECT id, name_ar, department_code FROM hr_departments WHERE parent_id = ? AND is_archived = 0 ORDER BY name_ar",
[(int) $id] [(int) $id]
); );
...@@ -110,7 +110,7 @@ class DepartmentController extends Controller ...@@ -110,7 +110,7 @@ class DepartmentController extends Controller
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$departments = HrDepartment::allActive(); $departments = HrDepartment::allActive();
$branches = $db->select("SELECT id, name_ar FROM branches WHERE is_archived = 0 ORDER BY name_ar"); $branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
$employees = HrEmployeeProfile::getActiveIds(); $employees = HrEmployeeProfile::getActiveIds();
return $this->view('HR.Views.departments.form', [ return $this->view('HR.Views.departments.form', [
...@@ -181,9 +181,9 @@ class DepartmentController extends Controller ...@@ -181,9 +181,9 @@ class DepartmentController extends Controller
return [ return [
'name_ar' => trim((string) $request->post('name_ar', '')), 'name_ar' => trim((string) $request->post('name_ar', '')),
'name_en' => trim((string) $request->post('name_en', '')) ?: null, 'name_en' => trim((string) $request->post('name_en', '')) ?: null,
'code' => strtoupper(trim((string) $request->post('code', ''))), 'department_code' => strtoupper(trim((string) $request->post('department_code', ''))),
'parent_id' => ((int) $request->post('parent_id', 0)) ?: null, 'parent_id' => ((int) $request->post('parent_id', 0)) ?: null,
'manager_id' => ((int) $request->post('manager_id', 0)) ?: null, 'manager_employee_id' => ((int) $request->post('manager_employee_id', 0)) ?: null,
'branch_id' => ((int) $request->post('branch_id', 0)) ?: null, 'branch_id' => ((int) $request->post('branch_id', 0)) ?: null,
'is_active' => (int) ($request->post('is_active', 1)), 'is_active' => (int) ($request->post('is_active', 1)),
]; ];
...@@ -195,7 +195,7 @@ class DepartmentController extends Controller ...@@ -195,7 +195,7 @@ class DepartmentController extends Controller
if ($data['name_ar'] === '' || mb_strlen($data['name_ar']) < 2) { if ($data['name_ar'] === '' || mb_strlen($data['name_ar']) < 2) {
$errors[] = 'اسم القسم بالعربي مطلوب (حرفان على الأقل)'; $errors[] = 'اسم القسم بالعربي مطلوب (حرفان على الأقل)';
} }
if ($data['code'] === '') { if ($data['department_code'] === '') {
$errors[] = 'كود القسم مطلوب'; $errors[] = 'كود القسم مطلوب';
} }
return $errors; return $errors;
......
...@@ -272,7 +272,7 @@ class EmployeeProfileController extends Controller ...@@ -272,7 +272,7 @@ class EmployeeProfileController extends Controller
// Check if detail already exists for this component+date // Check if detail already exists for this component+date
$existing = $db->selectOne( $existing = $db->selectOne(
"SELECT id FROM hr_employee_salary_details WHERE employee_profile_id = ? AND salary_component_id = ? AND effective_date = ? AND is_archived = 0", "SELECT id FROM hr_employee_salary_details WHERE employee_profile_id = ? AND component_id = ? AND effective_from = ? AND is_active = 1",
[(int) $id, (int) $componentId, $effectiveDate] [(int) $id, (int) $componentId, $effectiveDate]
); );
...@@ -285,9 +285,9 @@ class EmployeeProfileController extends Controller ...@@ -285,9 +285,9 @@ class EmployeeProfileController extends Controller
} else { } else {
$db->insert('hr_employee_salary_details', [ $db->insert('hr_employee_salary_details', [
'employee_profile_id' => (int) $id, 'employee_profile_id' => (int) $id,
'salary_component_id' => (int) $componentId, 'component_id' => (int) $componentId,
'amount' => $amount, 'amount' => $amount,
'effective_date' => $effectiveDate, 'effective_from' => $effectiveDate,
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
'created_by' => $employee ? (int) $employee->id : null, 'created_by' => $employee ? (int) $employee->id : null,
......
...@@ -202,7 +202,7 @@ class EndOfServiceController extends Controller ...@@ -202,7 +202,7 @@ class EndOfServiceController extends Controller
$db->update('hr_end_of_service', [ $db->update('hr_end_of_service', [
'status' => 'paid', 'status' => 'paid',
'payment_date' => $paymentDate, 'paid_date' => $paymentDate,
'updated_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
'updated_by' => $employee ? (int) $employee->id : null, 'updated_by' => $employee ? (int) $employee->id : null,
], '`id` = ?', [(int) $id]); ], '`id` = ?', [(int) $id]);
......
...@@ -92,7 +92,7 @@ class HolidayController extends Controller ...@@ -92,7 +92,7 @@ class HolidayController extends Controller
return [ return [
'name_ar' => trim((string) $request->post('name_ar', '')), 'name_ar' => trim((string) $request->post('name_ar', '')),
'name_en' => trim((string) $request->post('name_en', '')) ?: null, 'name_en' => trim((string) $request->post('name_en', '')) ?: null,
'holiday_date' => trim((string) $request->post('holiday_date', '')), 'date' => trim((string) $request->post('date', '')),
'duration_days' => max(1, (int) $request->post('duration_days', 1)), 'duration_days' => max(1, (int) $request->post('duration_days', 1)),
'holiday_type' => trim((string) $request->post('holiday_type', 'national')), 'holiday_type' => trim((string) $request->post('holiday_type', 'national')),
'is_recurring' => (int) ($request->post('is_recurring', 0)), 'is_recurring' => (int) ($request->post('is_recurring', 0)),
...@@ -107,7 +107,7 @@ class HolidayController extends Controller ...@@ -107,7 +107,7 @@ class HolidayController extends Controller
if ($data['name_ar'] === '' || mb_strlen($data['name_ar']) < 2) { if ($data['name_ar'] === '' || mb_strlen($data['name_ar']) < 2) {
$errors[] = 'اسم العطلة بالعربي مطلوب'; $errors[] = 'اسم العطلة بالعربي مطلوب';
} }
if ($data['holiday_date'] === '') { if ($data['date'] === '') {
$errors[] = 'تاريخ العطلة مطلوب'; $errors[] = 'تاريخ العطلة مطلوب';
} }
if (!in_array($data['holiday_type'], ['national', 'religious', 'company'], true)) { if (!in_array($data['holiday_type'], ['national', 'religious', 'company'], true)) {
......
...@@ -60,7 +60,7 @@ class HrReportController extends Controller ...@@ -60,7 +60,7 @@ class HrReportController extends Controller
"SELECT pp.month, "SELECT pp.month,
pp.status as period_status, pp.status as period_status,
COUNT(pr.id) as employee_count, COUNT(pr.id) as employee_count,
SUM(pr.gross_salary) as total_gross, SUM(pr.gross_earnings) as total_gross,
SUM(pr.total_deductions) as total_deductions, SUM(pr.total_deductions) as total_deductions,
SUM(pr.net_salary) as total_net, SUM(pr.net_salary) as total_net,
SUM(pr.overtime_amount) as total_overtime, SUM(pr.overtime_amount) as total_overtime,
...@@ -159,9 +159,9 @@ class HrReportController extends Controller ...@@ -159,9 +159,9 @@ class HrReportController extends Controller
$monthlySummary = $db->select( $monthlySummary = $db->select(
"SELECT pp.month, "SELECT pp.month,
COUNT(ir.id) as employee_count, COUNT(ir.id) as employee_count,
SUM(ir.employee_share_total) as total_employee_share, SUM(ir.total_employee_share) as total_employee_share,
SUM(ir.employer_share_total) as total_employer_share, SUM(ir.total_employer_share) as total_employer_share,
SUM(ir.employee_share_total + ir.employer_share_total) as total_combined SUM(ir.total_employee_share + ir.total_employer_share) as total_combined
FROM hr_payroll_periods pp FROM hr_payroll_periods pp
LEFT JOIN hr_insurance_records ir ON ir.period_id = pp.id AND ir.is_archived = 0 LEFT JOIN hr_insurance_records ir ON ir.period_id = pp.id AND ir.is_archived = 0
WHERE pp.year = ? AND pp.is_archived = 0 WHERE pp.year = ? AND pp.is_archived = 0
...@@ -185,9 +185,9 @@ class HrReportController extends Controller ...@@ -185,9 +185,9 @@ class HrReportController extends Controller
$monthlySummary = $db->select( $monthlySummary = $db->select(
"SELECT pp.month, "SELECT pp.month,
COUNT(tr.id) as employee_count, COUNT(tr.id) as employee_count,
SUM(tr.gross_taxable_income) as total_taxable, SUM(tr.gross_taxable_monthly) as total_taxable,
SUM(tr.total_exemptions) as total_exemptions, SUM(tr.personal_exemption + tr.insurance_exemption) as total_exemptions,
SUM(tr.tax_amount) as total_tax SUM(tr.monthly_tax) as total_tax
FROM hr_payroll_periods pp FROM hr_payroll_periods pp
LEFT JOIN hr_tax_records tr ON tr.period_id = pp.id AND tr.is_archived = 0 LEFT JOIN hr_tax_records tr ON tr.period_id = pp.id AND tr.is_archived = 0
WHERE pp.year = ? AND pp.is_archived = 0 WHERE pp.year = ? AND pp.is_archived = 0
...@@ -197,7 +197,7 @@ class HrReportController extends Controller ...@@ -197,7 +197,7 @@ class HrReportController extends Controller
); );
$ytdTotal = $db->selectOne( $ytdTotal = $db->selectOne(
"SELECT SUM(tr.tax_amount) as ytd_tax "SELECT SUM(tr.monthly_tax) as ytd_tax
FROM hr_tax_records tr FROM hr_tax_records tr
JOIN hr_payroll_periods pp ON pp.id = tr.period_id JOIN hr_payroll_periods pp ON pp.id = tr.period_id
WHERE pp.year = ? AND tr.is_archived = 0", WHERE pp.year = ? AND tr.is_archived = 0",
......
...@@ -21,8 +21,8 @@ class InsuranceController extends Controller ...@@ -21,8 +21,8 @@ class InsuranceController extends Controller
$periods = $db->select( $periods = $db->select(
"SELECT pp.id, pp.period_code, pp.year, pp.month, pp.status, "SELECT pp.id, pp.period_code, pp.year, pp.month, pp.status,
(SELECT COUNT(*) FROM hr_insurance_records ir WHERE ir.period_id = pp.id AND ir.is_archived = 0) as record_count, (SELECT COUNT(*) FROM hr_insurance_records ir WHERE ir.period_id = pp.id AND ir.is_archived = 0) as record_count,
(SELECT SUM(employee_share_total) FROM hr_insurance_records ir WHERE ir.period_id = pp.id AND ir.is_archived = 0) as total_employee_share, (SELECT SUM(total_employee_share) FROM hr_insurance_records ir WHERE ir.period_id = pp.id AND ir.is_archived = 0) as total_employee_share,
(SELECT SUM(employer_share_total) FROM hr_insurance_records ir WHERE ir.period_id = pp.id AND ir.is_archived = 0) as total_employer_share (SELECT SUM(total_employer_share) FROM hr_insurance_records ir WHERE ir.period_id = pp.id AND ir.is_archived = 0) as total_employer_share
FROM hr_payroll_periods pp FROM hr_payroll_periods pp
WHERE pp.year = ? AND pp.is_archived = 0 WHERE pp.year = ? AND pp.is_archived = 0
ORDER BY pp.month ASC", ORDER BY pp.month ASC",
......
...@@ -107,11 +107,11 @@ class JobTitleController extends Controller ...@@ -107,11 +107,11 @@ class JobTitleController extends Controller
return [ return [
'name_ar' => trim((string) $request->post('name_ar', '')), 'name_ar' => trim((string) $request->post('name_ar', '')),
'name_en' => trim((string) $request->post('name_en', '')) ?: null, 'name_en' => trim((string) $request->post('name_en', '')) ?: null,
'code' => strtoupper(trim((string) $request->post('code', ''))), 'title_code' => strtoupper(trim((string) $request->post('title_code', ''))),
'grade_level' => ((int) $request->post('grade_level', 0)) ?: null, 'grade_level' => ((int) $request->post('grade_level', 0)) ?: null,
'min_salary' => trim((string) $request->post('min_salary', '')) ?: null, 'min_salary' => trim((string) $request->post('min_salary', '')) ?: null,
'max_salary' => trim((string) $request->post('max_salary', '')) ?: null, 'max_salary' => trim((string) $request->post('max_salary', '')) ?: null,
'description' => trim((string) $request->post('description', '')) ?: null, 'description_ar'=> trim((string) $request->post('description_ar', '')) ?: null,
'is_active' => (int) ($request->post('is_active', 1)), 'is_active' => (int) ($request->post('is_active', 1)),
]; ];
} }
...@@ -122,7 +122,7 @@ class JobTitleController extends Controller ...@@ -122,7 +122,7 @@ class JobTitleController extends Controller
if ($data['name_ar'] === '' || mb_strlen($data['name_ar']) < 2) { if ($data['name_ar'] === '' || mb_strlen($data['name_ar']) < 2) {
$errors[] = 'اسم المسمى الوظيفي بالعربي مطلوب (حرفان على الأقل)'; $errors[] = 'اسم المسمى الوظيفي بالعربي مطلوب (حرفان على الأقل)';
} }
if ($data['code'] === '') { if ($data['title_code'] === '') {
$errors[] = 'كود المسمى الوظيفي مطلوب'; $errors[] = 'كود المسمى الوظيفي مطلوب';
} }
if ($data['min_salary'] !== null && $data['max_salary'] !== null) { if ($data['min_salary'] !== null && $data['max_salary'] !== null) {
......
...@@ -65,7 +65,7 @@ class LeaveController extends Controller ...@@ -65,7 +65,7 @@ class LeaveController extends Controller
'leave_type_id' => (int) $request->post('leave_type_id', 0), 'leave_type_id' => (int) $request->post('leave_type_id', 0),
'start_date' => trim((string) $request->post('start_date', '')), 'start_date' => trim((string) $request->post('start_date', '')),
'end_date' => trim((string) $request->post('end_date', '')), 'end_date' => trim((string) $request->post('end_date', '')),
'is_half_day' => (int) ($request->post('is_half_day', 0)), 'half_day' => (int) ($request->post('half_day', 0)),
'reason' => trim((string) $request->post('reason', '')) ?: null, 'reason' => trim((string) $request->post('reason', '')) ?: null,
]; ];
...@@ -267,7 +267,7 @@ class LeaveController extends Controller ...@@ -267,7 +267,7 @@ class LeaveController extends Controller
$employee = $this->currentEmployee(); $employee = $this->currentEmployee();
$balance = $db->selectOne( $balance = $db->selectOne(
"SELECT * FROM hr_leave_balances WHERE employee_profile_id = ? AND leave_type_id = ? AND year = ? AND is_archived = 0", "SELECT * FROM hr_leave_balances WHERE employee_profile_id = ? AND leave_type_id = ? AND year = ?",
[$profileId, $leaveTypeId, $year] [$profileId, $leaveTypeId, $year]
); );
......
...@@ -93,7 +93,7 @@ class PayrollController extends Controller ...@@ -93,7 +93,7 @@ class PayrollController extends Controller
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$totals = $db->selectOne( $totals = $db->selectOne(
"SELECT COUNT(*) as employee_count, "SELECT COUNT(*) as employee_count,
SUM(gross_salary) as total_gross, SUM(gross_earnings) as total_gross,
SUM(net_salary) as total_net, SUM(net_salary) as total_net,
SUM(total_deductions) as total_deductions SUM(total_deductions) as total_deductions
FROM hr_payroll_runs FROM hr_payroll_runs
...@@ -183,14 +183,14 @@ class PayrollController extends Controller ...@@ -183,14 +183,14 @@ class PayrollController extends Controller
try { try {
$db->update('hr_payroll_periods', [ $db->update('hr_payroll_periods', [
'status' => 'paid', 'status' => 'paid',
'payment_date' => $paymentDate, 'paid_date' => $paymentDate,
'updated_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
'updated_by' => $employee ? (int) $employee->id : null, 'updated_by' => $employee ? (int) $employee->id : null,
], '`id` = ?', [(int) $id]); ], '`id` = ?', [(int) $id]);
// Mark all runs as paid // Mark all runs as paid
$db->update('hr_payroll_runs', [ $db->update('hr_payroll_runs', [
'payment_status' => 'paid', 'status' => 'paid',
'paid_at' => $paymentDate . ' ' . date('H:i:s'), 'paid_at' => $paymentDate . ' ' . date('H:i:s'),
'updated_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
], '`period_id` = ? AND `is_archived` = 0', [(int) $id]); ], '`period_id` = ? AND `is_archived` = 0', [(int) $id]);
......
...@@ -22,8 +22,8 @@ class TaxController extends Controller ...@@ -22,8 +22,8 @@ class TaxController extends Controller
$monthlySummary = $db->select( $monthlySummary = $db->select(
"SELECT pp.month, "SELECT pp.month,
COUNT(tr.id) as employee_count, COUNT(tr.id) as employee_count,
SUM(tr.gross_taxable_income) as total_gross_taxable, SUM(tr.gross_taxable_monthly) as total_gross_taxable,
SUM(tr.tax_amount) as total_tax SUM(tr.monthly_tax) as total_tax
FROM hr_payroll_periods pp FROM hr_payroll_periods pp
LEFT JOIN hr_tax_records tr ON tr.period_id = pp.id AND tr.is_archived = 0 LEFT JOIN hr_tax_records tr ON tr.period_id = pp.id AND tr.is_archived = 0
WHERE pp.year = ? AND pp.is_archived = 0 WHERE pp.year = ? AND pp.is_archived = 0
...@@ -34,8 +34,8 @@ class TaxController extends Controller ...@@ -34,8 +34,8 @@ class TaxController extends Controller
// YTD totals // YTD totals
$ytdTotals = $db->selectOne( $ytdTotals = $db->selectOne(
"SELECT SUM(tr.gross_taxable_income) as ytd_gross_taxable, "SELECT SUM(tr.gross_taxable_monthly) as ytd_gross_taxable,
SUM(tr.tax_amount) as ytd_tax SUM(tr.monthly_tax) as ytd_tax
FROM hr_tax_records tr FROM hr_tax_records tr
JOIN hr_payroll_periods pp ON pp.id = tr.period_id JOIN hr_payroll_periods pp ON pp.id = tr.period_id
WHERE pp.year = ? AND tr.is_archived = 0 AND pp.is_archived = 0", WHERE pp.year = ? AND tr.is_archived = 0 AND pp.is_archived = 0",
......
...@@ -16,7 +16,7 @@ class WorkScheduleController extends Controller ...@@ -16,7 +16,7 @@ class WorkScheduleController extends Controller
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$schedules = $db->select( $schedules = $db->select(
"SELECT ws.*, "SELECT ws.*,
(SELECT COUNT(*) FROM hr_employee_schedules es WHERE es.schedule_id = ws.id AND es.is_active = 1 AND es.is_archived = 0) as employee_count (SELECT COUNT(*) FROM hr_employee_schedules es WHERE es.schedule_id = ws.id AND es.is_active = 1) as employee_count
FROM hr_work_schedules ws FROM hr_work_schedules ws
WHERE ws.is_archived = 0 WHERE ws.is_archived = 0
ORDER BY ws.name_ar ASC" ORDER BY ws.name_ar ASC"
...@@ -104,7 +104,7 @@ class WorkScheduleController extends Controller ...@@ -104,7 +104,7 @@ class WorkScheduleController extends Controller
'hours_per_week' => trim((string) $request->post('hours_per_week', '48.00')), 'hours_per_week' => trim((string) $request->post('hours_per_week', '48.00')),
'ramadan_hours_per_day' => trim((string) $request->post('ramadan_hours_per_day', '')) ?: null, 'ramadan_hours_per_day' => trim((string) $request->post('ramadan_hours_per_day', '')) ?: null,
'is_night_shift' => (int) ($request->post('is_night_shift', 0)), 'is_night_shift' => (int) ($request->post('is_night_shift', 0)),
'days_config' => json_encode($daysConfig, JSON_UNESCAPED_UNICODE), 'days_config_json' => json_encode($daysConfig, JSON_UNESCAPED_UNICODE),
'is_active' => (int) ($request->post('is_active', 1)), 'is_active' => (int) ($request->post('is_active', 1)),
]; ];
} }
......
...@@ -16,11 +16,12 @@ class HrAttendance extends Model ...@@ -16,11 +16,12 @@ class HrAttendance extends Model
protected static bool $autoTrackAuthor = true; protected static bool $autoTrackAuthor = true;
protected static array $fillable = [ protected static array $fillable = [
'employee_profile_id', 'attendance_date', 'schedule_id', 'employee_profile_id', 'attendance_date',
'check_in_time', 'check_out_time', 'check_in_method', 'check_out_method', 'check_in_time', 'check_out_time', 'check_in_method', 'check_out_method',
'expected_hours', 'actual_hours', 'overtime_hours', 'actual_hours', 'overtime_hours', 'overtime_type',
'late_minutes', 'early_leave_minutes', 'late_minutes', 'early_leave_minutes',
'status', 'is_ramadan', 'notes', 'status', 'is_ramadan_schedule', 'notes',
'is_approved', 'approved_by', 'approved_at',
]; ];
public static function getStatuses(): array public static function getStatuses(): array
...@@ -130,12 +131,12 @@ class HrAttendance extends Model ...@@ -130,12 +131,12 @@ class HrAttendance extends Model
$offset = ($page - 1) * $perPage; $offset = ($page - 1) * $perPage;
$rows = $db->select( $rows = $db->select(
"SELECT a.*, p.full_name_ar, p.employee_number, d.name_ar as department_name "SELECT a.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number, d.name_ar as department_name
FROM hr_attendance a FROM hr_attendance a
JOIN hr_employee_profiles p ON p.id = a.employee_profile_id JOIN hr_employee_profiles p ON p.id = a.employee_profile_id
LEFT JOIN hr_departments d ON d.id = p.department_id LEFT JOIN hr_departments d ON d.id = p.department_id
WHERE {$where} WHERE {$where}
ORDER BY a.attendance_date DESC, p.full_name_ar ASC ORDER BY a.attendance_date DESC, p.first_name_ar ASC
LIMIT {$perPage} OFFSET {$offset}", LIMIT {$perPage} OFFSET {$offset}",
$params $params
); );
......
...@@ -19,9 +19,11 @@ class HrContract extends Model ...@@ -19,9 +19,11 @@ class HrContract extends Model
'employee_profile_id', 'contract_number', 'contract_type', 'employee_profile_id', 'contract_number', 'contract_type',
'start_date', 'end_date', 'probation_months', 'start_date', 'end_date', 'probation_months',
'working_hours_per_day', 'working_days_per_week', 'working_hours_per_day', 'working_days_per_week',
'notice_period_months', 'basic_salary', 'total_salary', 'notice_period_months', 'basic_salary', 'total_package',
'salary_structure_id', 'previous_contract_id', 'terms_json', 'renewal_of_contract_id',
'signed_copy_document_id', 'status', 'notes', 'workflow_instance_id', 'signed_date',
'signed_by_employee', 'signed_by_employer',
'document_path', 'status', 'notes',
]; ];
public static function getContractTypes(): array public static function getContractTypes(): array
...@@ -58,7 +60,7 @@ class HrContract extends Model ...@@ -58,7 +60,7 @@ class HrContract extends Model
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$futureDate = date('Y-m-d', strtotime("+{$days} days")); $futureDate = date('Y-m-d', strtotime("+{$days} days"));
return $db->select( return $db->select(
"SELECT c.*, p.full_name_ar, p.employee_number "SELECT c.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number
FROM hr_contracts c FROM hr_contracts c
JOIN hr_employee_profiles p ON p.id = c.employee_profile_id JOIN hr_employee_profiles p ON p.id = c.employee_profile_id
WHERE c.status = 'active' WHERE c.status = 'active'
...@@ -80,7 +82,7 @@ class HrContract extends Model ...@@ -80,7 +82,7 @@ class HrContract extends Model
if (!empty($filters['q'])) { if (!empty($filters['q'])) {
$search = '%' . $filters['q'] . '%'; $search = '%' . $filters['q'] . '%';
$where .= ' AND (p.full_name_ar LIKE ? OR c.contract_number LIKE ?)'; $where .= ' AND (CONCAT(p.first_name_ar, \' \', p.last_name_ar) LIKE ? OR c.contract_number LIKE ?)';
$params[] = $search; $params[] = $search;
$params[] = $search; $params[] = $search;
} }
...@@ -101,7 +103,7 @@ class HrContract extends Model ...@@ -101,7 +103,7 @@ class HrContract extends Model
$offset = ($page - 1) * $perPage; $offset = ($page - 1) * $perPage;
$rows = $db->select( $rows = $db->select(
"SELECT c.*, p.full_name_ar, p.employee_number "SELECT c.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number
FROM hr_contracts c FROM hr_contracts c
JOIN hr_employee_profiles p ON p.id = c.employee_profile_id JOIN hr_employee_profiles p ON p.id = c.employee_profile_id
WHERE {$where} WHERE {$where}
......
...@@ -16,8 +16,8 @@ class HrDepartment extends Model ...@@ -16,8 +16,8 @@ class HrDepartment extends Model
protected static bool $autoTrackAuthor = true; protected static bool $autoTrackAuthor = true;
protected static array $fillable = [ protected static array $fillable = [
'code', 'name_ar', 'name_en', 'parent_id', 'manager_employee_id', 'department_code', 'name_ar', 'name_en', 'parent_id', 'manager_employee_id',
'branch_id', 'description', 'is_active', 'branch_id', 'description_ar', 'is_active', 'sort_order',
]; ];
public static function allActive(): array public static function allActive(): array
...@@ -56,7 +56,7 @@ class HrDepartment extends Model ...@@ -56,7 +56,7 @@ class HrDepartment extends Model
if (!empty($filters['q'])) { if (!empty($filters['q'])) {
$search = '%' . $filters['q'] . '%'; $search = '%' . $filters['q'] . '%';
$where .= ' AND (d.name_ar LIKE ? OR d.name_en LIKE ? OR d.code LIKE ?)'; $where .= ' AND (d.name_ar LIKE ? OR d.name_en LIKE ? OR d.department_code LIKE ?)';
$params[] = $search; $params[] = $search;
$params[] = $search; $params[] = $search;
$params[] = $search; $params[] = $search;
......
...@@ -114,7 +114,7 @@ class HrDisciplinaryAction extends Model ...@@ -114,7 +114,7 @@ class HrDisciplinaryAction extends Model
$offset = ($page - 1) * $perPage; $offset = ($page - 1) * $perPage;
$rows = $db->select( $rows = $db->select(
"SELECT da.*, p.full_name_ar, p.employee_number "SELECT da.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number
FROM hr_disciplinary_actions da FROM hr_disciplinary_actions da
JOIN hr_employee_profiles p ON p.id = da.employee_profile_id JOIN hr_employee_profiles p ON p.id = da.employee_profile_id
WHERE {$where} WHERE {$where}
......
...@@ -54,7 +54,7 @@ class HrEmployeeDocument extends Model ...@@ -54,7 +54,7 @@ class HrEmployeeDocument extends Model
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$futureDate = date('Y-m-d', strtotime("+{$days} days")); $futureDate = date('Y-m-d', strtotime("+{$days} days"));
return $db->select( return $db->select(
"SELECT d.*, p.full_name_ar, p.employee_number "SELECT d.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number
FROM hr_employee_documents d FROM hr_employee_documents d
JOIN hr_employee_profiles p ON p.id = d.employee_profile_id JOIN hr_employee_profiles p ON p.id = d.employee_profile_id
WHERE d.expiry_date IS NOT NULL WHERE d.expiry_date IS NOT NULL
...@@ -93,7 +93,7 @@ class HrEmployeeDocument extends Model ...@@ -93,7 +93,7 @@ class HrEmployeeDocument extends Model
$offset = ($page - 1) * $perPage; $offset = ($page - 1) * $perPage;
$rows = $db->select( $rows = $db->select(
"SELECT d.*, p.full_name_ar, p.employee_number "SELECT d.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number
FROM hr_employee_documents d FROM hr_employee_documents d
JOIN hr_employee_profiles p ON p.id = d.employee_profile_id JOIN hr_employee_profiles p ON p.id = d.employee_profile_id
WHERE {$where} WHERE {$where}
......
...@@ -104,7 +104,7 @@ class HrEmployeeLoan extends Model ...@@ -104,7 +104,7 @@ class HrEmployeeLoan extends Model
$offset = ($page - 1) * $perPage; $offset = ($page - 1) * $perPage;
$rows = $db->select( $rows = $db->select(
"SELECT el.*, p.full_name_ar, p.employee_number "SELECT el.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number
FROM hr_employee_loans el FROM hr_employee_loans el
JOIN hr_employee_profiles p ON p.id = el.employee_profile_id JOIN hr_employee_profiles p ON p.id = el.employee_profile_id
WHERE {$where} WHERE {$where}
......
...@@ -16,17 +16,19 @@ class HrEmployeeProfile extends Model ...@@ -16,17 +16,19 @@ class HrEmployeeProfile extends Model
protected static bool $autoTrackAuthor = true; protected static bool $autoTrackAuthor = true;
protected static array $fillable = [ protected static array $fillable = [
'employee_id', 'employee_number', 'national_id', 'full_name_ar', 'full_name_en', 'employee_id', 'employee_number', 'national_id',
'first_name_ar', 'last_name_ar', 'first_name_en', 'last_name_en',
'date_of_birth', 'gender', 'marital_status', 'religion', 'nationality', 'date_of_birth', 'gender', 'marital_status', 'religion', 'nationality',
'phone', 'email', 'address', 'phone', 'email', 'address',
'department_id', 'job_title_id', 'salary_structure_id', 'department_id', 'job_title_id', 'direct_manager_id', 'salary_structure_id',
'hire_date', 'probation_end_date', 'employment_type', 'employment_status', 'hire_date', 'probation_end_date', 'probation_status',
'employment_type', 'employment_status',
'basic_salary', 'insurable_salary', 'basic_salary', 'insurable_salary',
'bank_name', 'bank_account_number', 'bank_iban', 'bank_name', 'bank_account_number', 'bank_iban',
'insurance_number', 'insurance_start_date', 'insurance_number', 'insurance_start_date', 'tax_card_number',
'military_status', 'has_disability', 'disability_description', 'military_status', 'has_disability', 'disability_details',
'emergency_contact_name', 'emergency_contact_phone', 'emergency_contact_relation', 'emergency_contact_name', 'emergency_contact_phone',
'notes', 'photo_path', 'notes',
]; ];
public static function getEmploymentStatuses(): array public static function getEmploymentStatuses(): array
...@@ -97,7 +99,7 @@ class HrEmployeeProfile extends Model ...@@ -97,7 +99,7 @@ class HrEmployeeProfile extends Model
{ {
return static::query() return static::query()
->where('employment_status', '=', 'active') ->where('employment_status', '=', 'active')
->orderBy('full_name_ar', 'ASC') ->orderBy('first_name_ar', 'ASC')
->get(); ->get();
} }
...@@ -118,7 +120,9 @@ class HrEmployeeProfile extends Model ...@@ -118,7 +120,9 @@ class HrEmployeeProfile extends Model
if (!empty($filters['q'])) { if (!empty($filters['q'])) {
$search = '%' . $filters['q'] . '%'; $search = '%' . $filters['q'] . '%';
$where .= ' AND (p.full_name_ar LIKE ? OR p.full_name_en LIKE ? OR p.employee_number LIKE ? OR p.national_id LIKE ?)'; $where .= ' AND (p.first_name_ar LIKE ? OR p.last_name_ar LIKE ? OR p.first_name_en LIKE ? OR p.last_name_en LIKE ? OR p.employee_number LIKE ? OR p.national_id LIKE ?)';
$params[] = $search;
$params[] = $search;
$params[] = $search; $params[] = $search;
$params[] = $search; $params[] = $search;
$params[] = $search; $params[] = $search;
...@@ -147,7 +151,7 @@ class HrEmployeeProfile extends Model ...@@ -147,7 +151,7 @@ class HrEmployeeProfile extends Model
LEFT JOIN hr_departments d ON d.id = p.department_id LEFT JOIN hr_departments d ON d.id = p.department_id
LEFT JOIN hr_job_titles j ON j.id = p.job_title_id LEFT JOIN hr_job_titles j ON j.id = p.job_title_id
WHERE {$where} WHERE {$where}
ORDER BY p.full_name_ar ASC ORDER BY p.first_name_ar ASC
LIMIT {$perPage} OFFSET {$offset}", LIMIT {$perPage} OFFSET {$offset}",
$params $params
); );
......
...@@ -15,8 +15,8 @@ class HrEmployeeSalaryDetail extends Model ...@@ -15,8 +15,8 @@ class HrEmployeeSalaryDetail extends Model
protected static bool $autoTrackAuthor = true; protected static bool $autoTrackAuthor = true;
protected static array $fillable = [ protected static array $fillable = [
'employee_profile_id', 'component_id', 'amount', 'effective_from', 'employee_profile_id', 'component_id', 'amount', 'percentage',
'effective_to', 'notes', 'effective_from', 'effective_to', 'is_active',
]; ];
public static function getForEmployee(int $profileId, ?string $asOfDate = null): array public static function getForEmployee(int $profileId, ?string $asOfDate = null): array
......
...@@ -77,7 +77,7 @@ class HrEndOfService extends Model ...@@ -77,7 +77,7 @@ class HrEndOfService extends Model
$offset = ($page - 1) * $perPage; $offset = ($page - 1) * $perPage;
$rows = $db->select( $rows = $db->select(
"SELECT eos.*, p.full_name_ar, p.employee_number "SELECT eos.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number
FROM hr_end_of_service eos FROM hr_end_of_service eos
JOIN hr_employee_profiles p ON p.employee_id = eos.employee_id JOIN hr_employee_profiles p ON p.employee_id = eos.employee_id
WHERE {$where} WHERE {$where}
......
...@@ -19,18 +19,18 @@ class HrInsuranceRecord extends Model ...@@ -19,18 +19,18 @@ class HrInsuranceRecord extends Model
'basic_insurable_salary', 'variable_insurable_salary', 'basic_insurable_salary', 'variable_insurable_salary',
'employee_basic_share', 'employee_variable_share', 'total_employee_share', 'employee_basic_share', 'employee_variable_share', 'total_employee_share',
'employer_basic_share', 'employer_variable_share', 'total_employer_share', 'employer_basic_share', 'employer_variable_share', 'total_employer_share',
'total_contribution', 'notes', 'total_contribution', 'insurance_number', 'notes',
]; ];
public static function getForPeriod(int $periodId): array public static function getForPeriod(int $periodId): array
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
return $db->select( return $db->select(
"SELECT ir.*, p.full_name_ar, p.employee_number, p.insurance_number "SELECT ir.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number, p.insurance_number
FROM hr_insurance_records ir FROM hr_insurance_records ir
JOIN hr_employee_profiles p ON p.id = ir.employee_profile_id JOIN hr_employee_profiles p ON p.id = ir.employee_profile_id
WHERE ir.period_id = ? WHERE ir.period_id = ?
ORDER BY p.full_name_ar ASC", ORDER BY p.first_name_ar ASC",
[$periodId] [$periodId]
); );
} }
...@@ -62,8 +62,8 @@ class HrInsuranceRecord extends Model ...@@ -62,8 +62,8 @@ class HrInsuranceRecord extends Model
$row = $db->selectOne( $row = $db->selectOne(
"SELECT "SELECT
COUNT(*) as employee_count, COUNT(*) as employee_count,
COALESCE(SUM(total_employee_share), 0) as total_employee, COALESCE(SUM(total_employee_share), 0) as total_employee_share,
COALESCE(SUM(total_employer_share), 0) as total_employer, COALESCE(SUM(total_employer_share), 0) as total_employer_share,
COALESCE(SUM(total_contribution), 0) as grand_total COALESCE(SUM(total_contribution), 0) as grand_total
FROM hr_insurance_records WHERE period_id = ?", FROM hr_insurance_records WHERE period_id = ?",
[$periodId] [$periodId]
......
...@@ -16,8 +16,8 @@ class HrJobTitle extends Model ...@@ -16,8 +16,8 @@ class HrJobTitle extends Model
protected static bool $autoTrackAuthor = true; protected static bool $autoTrackAuthor = true;
protected static array $fillable = [ protected static array $fillable = [
'code', 'name_ar', 'name_en', 'grade_level', 'min_salary', 'title_code', 'name_ar', 'name_en', 'grade_level', 'min_salary',
'max_salary', 'description', 'is_active', 'max_salary', 'description_ar', 'is_active',
]; ];
public static function allActive(): array public static function allActive(): array
...@@ -37,7 +37,7 @@ class HrJobTitle extends Model ...@@ -37,7 +37,7 @@ class HrJobTitle extends Model
if (!empty($filters['q'])) { if (!empty($filters['q'])) {
$search = '%' . $filters['q'] . '%'; $search = '%' . $filters['q'] . '%';
$where .= ' AND (name_ar LIKE ? OR name_en LIKE ? OR code LIKE ?)'; $where .= ' AND (name_ar LIKE ? OR name_en LIKE ? OR title_code LIKE ?)';
$params[] = $search; $params[] = $search;
$params[] = $search; $params[] = $search;
$params[] = $search; $params[] = $search;
......
...@@ -17,7 +17,7 @@ class HrLeaveBalance extends Model ...@@ -17,7 +17,7 @@ class HrLeaveBalance extends Model
protected static array $fillable = [ protected static array $fillable = [
'employee_profile_id', 'leave_type_id', 'year', 'employee_profile_id', 'leave_type_id', 'year',
'entitled_days', 'carried_over_days', 'adjustment_days', 'entitled_days', 'carried_over_days', 'adjustment_days',
'used_days', 'pending_days', 'notes', 'used_days', 'pending_days', 'adjustment_reason',
]; ];
public static function getForEmployee(int $profileId, int $year): array public static function getForEmployee(int $profileId, int $year): array
......
...@@ -17,9 +17,10 @@ class HrLeaveRequest extends Model ...@@ -17,9 +17,10 @@ class HrLeaveRequest extends Model
protected static array $fillable = [ protected static array $fillable = [
'employee_profile_id', 'leave_type_id', 'start_date', 'end_date', 'employee_profile_id', 'leave_type_id', 'start_date', 'end_date',
'total_days', 'is_half_day', 'half_day_period', 'reason', 'total_days', 'half_day', 'half_day_type', 'reason',
'attachment_path', 'status', 'workflow_instance_id', 'document_path', 'status', 'workflow_instance_id',
'approved_by', 'approved_at', 'rejection_reason', 'notes', 'approved_by', 'approved_at', 'rejection_reason',
'return_date', 'notes',
]; ];
public static function getStatuses(): array public static function getStatuses(): array
...@@ -73,7 +74,7 @@ class HrLeaveRequest extends Model ...@@ -73,7 +74,7 @@ class HrLeaveRequest extends Model
$offset = ($page - 1) * $perPage; $offset = ($page - 1) * $perPage;
$rows = $db->select( $rows = $db->select(
"SELECT lr.*, p.full_name_ar, p.employee_number, lt.name_ar as leave_type_name, d.name_ar as department_name "SELECT lr.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number, lt.name_ar as leave_type_name, d.name_ar as department_name
FROM hr_leave_requests lr FROM hr_leave_requests lr
JOIN hr_employee_profiles p ON p.id = lr.employee_profile_id JOIN hr_employee_profiles p ON p.id = lr.employee_profile_id
JOIN hr_leave_types lt ON lt.id = lr.leave_type_id JOIN hr_leave_types lt ON lt.id = lr.leave_type_id
...@@ -91,7 +92,7 @@ class HrLeaveRequest extends Model ...@@ -91,7 +92,7 @@ class HrLeaveRequest extends Model
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
return $db->select( return $db->select(
"SELECT lr.*, p.full_name_ar, p.employee_number, lt.name_ar as leave_type_name "SELECT lr.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number, lt.name_ar as leave_type_name
FROM hr_leave_requests lr FROM hr_leave_requests lr
JOIN hr_employee_profiles p ON p.id = lr.employee_profile_id JOIN hr_employee_profiles p ON p.id = lr.employee_profile_id
JOIN hr_leave_types lt ON lt.id = lr.leave_type_id JOIN hr_leave_types lt ON lt.id = lr.leave_type_id
......
...@@ -16,7 +16,7 @@ class HrLoanInstallment extends Model ...@@ -16,7 +16,7 @@ class HrLoanInstallment extends Model
protected static array $fillable = [ protected static array $fillable = [
'loan_id', 'installment_number', 'due_date', 'amount', 'loan_id', 'installment_number', 'due_date', 'amount',
'paid_amount', 'paid_date', 'payroll_run_id', 'status', 'notes', 'payroll_run_id', 'deducted_at', 'status', 'notes',
]; ];
public static function getForLoan(int $loanId): array public static function getForLoan(int $loanId): array
...@@ -36,14 +36,13 @@ class HrLoanInstallment extends Model ...@@ -36,14 +36,13 @@ class HrLoanInstallment extends Model
); );
} }
public static function markPaid(int $installmentId, string $amount, ?int $payrollRunId = null): void public static function markDeducted(int $installmentId, ?int $payrollRunId = null): void
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$db->update('hr_loan_installments', [ $db->update('hr_loan_installments', [
'paid_amount' => $amount,
'paid_date' => date('Y-m-d'),
'payroll_run_id' => $payrollRunId, 'payroll_run_id' => $payrollRunId,
'status' => 'paid', 'deducted_at' => date('Y-m-d H:i:s'),
'status' => 'deducted',
'updated_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [$installmentId]); ], '`id` = ?', [$installmentId]);
} }
......
...@@ -30,12 +30,12 @@ class HrPayrollRun extends Model ...@@ -30,12 +30,12 @@ class HrPayrollRun extends Model
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
return $db->select( return $db->select(
"SELECT pr.*, p.full_name_ar, p.employee_number, d.name_ar as department_name "SELECT pr.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number, d.name_ar as department_name
FROM hr_payroll_runs pr FROM hr_payroll_runs pr
JOIN hr_employee_profiles p ON p.id = pr.employee_profile_id JOIN hr_employee_profiles p ON p.id = pr.employee_profile_id
LEFT JOIN hr_departments d ON d.id = p.department_id LEFT JOIN hr_departments d ON d.id = p.department_id
WHERE pr.period_id = ? WHERE pr.period_id = ?
ORDER BY p.full_name_ar ASC", ORDER BY p.first_name_ar ASC",
[$periodId] [$periodId]
); );
} }
...@@ -65,7 +65,7 @@ class HrPayrollRun extends Model ...@@ -65,7 +65,7 @@ class HrPayrollRun extends Model
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
return $db->select( return $db->select(
"SELECT * FROM hr_payroll_components_log WHERE payroll_run_id = ? ORDER BY sort_order ASC", "SELECT *, name_ar as component_name FROM hr_payroll_components_log WHERE payroll_run_id = ? ORDER BY sort_order ASC",
[$runId] [$runId]
); );
} }
......
...@@ -50,14 +50,14 @@ class HrPerformanceCycle extends Model ...@@ -50,14 +50,14 @@ class HrPerformanceCycle extends Model
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
return $db->select( return $db->select(
"SELECT pr.*, p.full_name_ar, p.employee_number, d.name_ar as department_name, "SELECT pr.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number, d.name_ar as department_name,
rev.full_name_ar as reviewer_name CONCAT(rev.first_name_ar, ' ', rev.last_name_ar) as reviewer_name
FROM hr_performance_reviews pr FROM hr_performance_reviews pr
JOIN hr_employee_profiles p ON p.employee_id = pr.employee_id JOIN hr_employee_profiles p ON p.employee_id = pr.employee_id
LEFT JOIN hr_departments d ON d.id = p.department_id LEFT JOIN hr_departments d ON d.id = p.department_id
LEFT JOIN hr_employee_profiles rev ON rev.employee_id = pr.reviewer_id LEFT JOIN hr_employee_profiles rev ON rev.employee_id = pr.reviewer_id
WHERE pr.cycle_id = ? WHERE pr.cycle_id = ?
ORDER BY p.full_name_ar ASC", ORDER BY p.first_name_ar ASC",
[$cycleId] [$cycleId]
); );
} }
......
...@@ -58,7 +58,7 @@ class HrPerformanceReview extends Model ...@@ -58,7 +58,7 @@ class HrPerformanceReview extends Model
$db = App::getInstance()->db(); $db = App::getInstance()->db();
return $db->select( return $db->select(
"SELECT pr.*, pc.cycle_code, pc.name_ar as cycle_name, pc.year, "SELECT pr.*, pc.cycle_code, pc.name_ar as cycle_name, pc.year,
rev.full_name_ar as reviewer_name CONCAT(rev.first_name_ar, ' ', rev.last_name_ar) as reviewer_name
FROM hr_performance_reviews pr FROM hr_performance_reviews pr
JOIN hr_performance_cycles pc ON pc.id = pr.cycle_id JOIN hr_performance_cycles pc ON pc.id = pr.cycle_id
LEFT JOIN hr_employee_profiles rev ON rev.employee_id = pr.reviewer_id LEFT JOIN hr_employee_profiles rev ON rev.employee_id = pr.reviewer_id
......
...@@ -80,7 +80,7 @@ class HrSalaryAdjustment extends Model ...@@ -80,7 +80,7 @@ class HrSalaryAdjustment extends Model
$offset = ($page - 1) * $perPage; $offset = ($page - 1) * $perPage;
$rows = $db->select( $rows = $db->select(
"SELECT sa.*, p.full_name_ar, p.employee_number "SELECT sa.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number
FROM hr_salary_adjustments sa FROM hr_salary_adjustments sa
JOIN hr_employee_profiles p ON p.employee_id = sa.employee_id JOIN hr_employee_profiles p ON p.employee_id = sa.employee_id
WHERE {$where} WHERE {$where}
......
...@@ -26,11 +26,11 @@ class HrTaxRecord extends Model ...@@ -26,11 +26,11 @@ class HrTaxRecord extends Model
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
return $db->select( return $db->select(
"SELECT tr.*, p.full_name_ar, p.employee_number "SELECT tr.*, CONCAT(p.first_name_ar, ' ', p.last_name_ar) as full_name_ar, p.employee_number
FROM hr_tax_records tr FROM hr_tax_records tr
JOIN hr_employee_profiles p ON p.employee_id = tr.employee_id JOIN hr_employee_profiles p ON p.employee_id = tr.employee_id
WHERE tr.period_id = ? WHERE tr.period_id = ?
ORDER BY p.full_name_ar ASC", ORDER BY p.first_name_ar ASC",
[$periodId] [$periodId]
); );
} }
......
...@@ -36,17 +36,15 @@ final class AttendanceService ...@@ -36,17 +36,15 @@ final class AttendanceService
} }
$scheduleData = self::getScheduleForEmployee($profileId, $date); $scheduleData = self::getScheduleForEmployee($profileId, $date);
$expectedHours = self::getExpectedHours($profile, $scheduleData, $date);
$isRamadan = self::isRamadanDay($date, $profile->religion ?? null); $isRamadan = self::isRamadanDay($date, $profile->religion ?? null);
if ($existing) { if ($existing) {
$db->update('hr_attendance', [ $db->update('hr_attendance', [
'check_in_time' => $time, 'check_in_time' => $time,
'check_in_method' => $method, 'check_in_method' => $method,
'expected_hours' => $expectedHours, 'is_ramadan_schedule' => $isRamadan ? 1 : 0,
'is_ramadan' => $isRamadan ? 1 : 0, 'status' => 'present',
'status' => 'present', 'updated_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [(int) $existing['id']]); ], '`id` = ?', [(int) $existing['id']]);
} else { } else {
$isHoliday = HrHoliday::isHoliday($date, $profile->religion); $isHoliday = HrHoliday::isHoliday($date, $profile->religion);
...@@ -59,12 +57,10 @@ final class AttendanceService ...@@ -59,12 +57,10 @@ final class AttendanceService
$db->insert('hr_attendance', [ $db->insert('hr_attendance', [
'employee_profile_id' => $profileId, 'employee_profile_id' => $profileId,
'attendance_date' => $date, 'attendance_date' => $date,
'schedule_id' => $scheduleData['id'] ?? null,
'check_in_time' => $time, 'check_in_time' => $time,
'check_in_method' => $method, 'check_in_method' => $method,
'expected_hours' => $expectedHours,
'status' => $status, 'status' => $status,
'is_ramadan' => $isRamadan ? 1 : 0, 'is_ramadan_schedule' => $isRamadan ? 1 : 0,
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
]); ]);
...@@ -92,19 +88,20 @@ final class AttendanceService ...@@ -92,19 +88,20 @@ final class AttendanceService
$checkOut->modify('+1 day'); $checkOut->modify('+1 day');
} }
$diffMinutes = (int) (($checkOut->getTimestamp() - $checkIn->getTimestamp()) / 60); $diffMinutes = (int) (($checkOut->getTimestamp() - $checkIn->getTimestamp()) / 60);
$breakMinutes = (int) ($existing['break_minutes'] ?? 60); $scheduleData = self::getScheduleForEmployee($profileId, $date);
$breakMinutes = (int) ($scheduleData['break_duration_minutes'] ?? 60);
$actualMinutes = max(0, $diffMinutes - $breakMinutes); $actualMinutes = max(0, $diffMinutes - $breakMinutes);
$actualHours = number_format($actualMinutes / 60, 2, '.', ''); $actualHours = bcdiv((string) $actualMinutes, '60', 2);
// Calculate overtime // Calculate overtime
$expectedHours = (float) ($existing['expected_hours'] ?? 8); $profile = HrEmployeeProfile::find($profileId);
$expectedHours = (float) self::getExpectedHours($profile, $scheduleData, $date);
$overtimeHours = '0.00'; $overtimeHours = '0.00';
if ((float) $actualHours > $expectedHours) { if (bccomp($actualHours, (string) $expectedHours, 2) > 0) {
$overtimeHours = number_format((float) $actualHours - $expectedHours, 2, '.', ''); $overtimeHours = bcsub($actualHours, (string) $expectedHours, 2);
} }
// Calculate late minutes // Calculate late minutes
$scheduleData = self::getScheduleForEmployee($profileId, $date);
$lateMinutes = 0; $lateMinutes = 0;
$earlyLeaveMinutes = 0; $earlyLeaveMinutes = 0;
......
...@@ -57,9 +57,8 @@ final class ContractService ...@@ -57,9 +57,8 @@ final class ContractService
'working_days_per_week' => $data['working_days_per_week'] ?? 6, 'working_days_per_week' => $data['working_days_per_week'] ?? 6,
'notice_period_months' => $data['notice_period_months'] ?? 2, 'notice_period_months' => $data['notice_period_months'] ?? 2,
'basic_salary' => $data['basic_salary'] ?? '0.00', 'basic_salary' => $data['basic_salary'] ?? '0.00',
'total_salary' => $data['total_salary'] ?? '0.00', 'total_package' => $data['total_package'] ?? '0.00',
'salary_structure_id' => $data['salary_structure_id'] ?? null, 'renewal_of_contract_id' => $data['renewal_of_contract_id'] ?? null,
'previous_contract_id' => $data['previous_contract_id'] ?? null,
'status' => 'draft', 'status' => 'draft',
'notes' => $data['notes'] ?? null, 'notes' => $data['notes'] ?? null,
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
...@@ -111,9 +110,8 @@ final class ContractService ...@@ -111,9 +110,8 @@ final class ContractService
'working_days_per_week' => $data['working_days_per_week'] ?? $contract['working_days_per_week'], 'working_days_per_week' => $data['working_days_per_week'] ?? $contract['working_days_per_week'],
'notice_period_months' => $data['notice_period_months'] ?? $contract['notice_period_months'], 'notice_period_months' => $data['notice_period_months'] ?? $contract['notice_period_months'],
'basic_salary' => $data['basic_salary'] ?? $contract['basic_salary'], 'basic_salary' => $data['basic_salary'] ?? $contract['basic_salary'],
'total_salary' => $data['total_salary'] ?? $contract['total_salary'], 'total_package' => $data['total_package'] ?? $contract['total_package'],
'salary_structure_id' => $data['salary_structure_id'] ?? $contract['salary_structure_id'], 'renewal_of_contract_id' => $contractId,
'previous_contract_id' => $contractId,
'status' => 'active', 'status' => 'active',
'notes' => $data['notes'] ?? null, 'notes' => $data['notes'] ?? null,
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
......
...@@ -75,7 +75,7 @@ final class LeaveService ...@@ -75,7 +75,7 @@ final class LeaveService
// Calculate total days // Calculate total days
$totalDays = self::calculateBusinessDays($startDate, $endDate, $profileId); $totalDays = self::calculateBusinessDays($startDate, $endDate, $profileId);
$isHalfDay = (int) ($data['is_half_day'] ?? 0); $isHalfDay = (int) ($data['half_day'] ?? 0);
if ($isHalfDay) { if ($isHalfDay) {
$totalDays = '0.5'; $totalDays = '0.5';
} }
...@@ -122,10 +122,10 @@ final class LeaveService ...@@ -122,10 +122,10 @@ final class LeaveService
'start_date' => $startDate, 'start_date' => $startDate,
'end_date' => $endDate, 'end_date' => $endDate,
'total_days' => $totalDays, 'total_days' => $totalDays,
'is_half_day' => $isHalfDay, 'half_day' => $isHalfDay,
'half_day_period' => $data['half_day_period'] ?? null, 'half_day_type' => $data['half_day_type'] ?? null,
'reason' => $data['reason'] ?? null, 'reason' => $data['reason'] ?? null,
'attachment_path' => $data['attachment_path'] ?? null, 'document_path' => $data['document_path'] ?? null,
'status' => 'pending', 'status' => 'pending',
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
...@@ -191,7 +191,7 @@ final class LeaveService ...@@ -191,7 +191,7 @@ final class LeaveService
EventBus::dispatch('hr.leave.approved', [ EventBus::dispatch('hr.leave.approved', [
'request_id' => $requestId, 'request_id' => $requestId,
'employee_id' => $profile ? $profile->employee_id : null, 'employee_id' => $profile ? $profile->employee_id : null,
'employee_name'=> $profile ? $profile->full_name_ar : '', 'employee_name'=> $profile ? trim(($profile->first_name_ar ?? '') . ' ' . ($profile->last_name_ar ?? '')) : '',
'leave_type' => $request['leave_type_id'], 'leave_type' => $request['leave_type_id'],
'leave_start' => $request['start_date'], 'leave_start' => $request['start_date'],
'leave_end' => $request['end_date'], 'leave_end' => $request['end_date'],
...@@ -243,7 +243,7 @@ final class LeaveService ...@@ -243,7 +243,7 @@ final class LeaveService
EventBus::dispatch('hr.leave.rejected', [ EventBus::dispatch('hr.leave.rejected', [
'request_id' => $requestId, 'request_id' => $requestId,
'employee_id' => $profile ? $profile->employee_id : null, 'employee_id' => $profile ? $profile->employee_id : null,
'employee_name' => $profile ? $profile->full_name_ar : '', 'employee_name' => $profile ? trim(($profile->first_name_ar ?? '') . ' ' . ($profile->last_name_ar ?? '')) : '',
]); ]);
return ['success' => true]; return ['success' => true];
......
...@@ -74,7 +74,6 @@ final class LoanService ...@@ -74,7 +74,6 @@ final class LoanService
'installment_number' => $i, 'installment_number' => $i,
'due_date' => $dueDate, 'due_date' => $dueDate,
'amount' => $amount, 'amount' => $amount,
'paid_amount' => '0.00',
'status' => 'pending', 'status' => 'pending',
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
...@@ -130,7 +129,7 @@ final class LoanService ...@@ -130,7 +129,7 @@ final class LoanService
$db->update('hr_employee_loans', [ $db->update('hr_employee_loans', [
'status' => 'disbursed', 'status' => 'disbursed',
'disbursed_date' => date('Y-m-d'), 'disbursed_date' => date('Y-m-d'),
'updated_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [$loanId]); ], '`id` = ?', [$loanId]);
...@@ -153,7 +152,7 @@ final class LoanService ...@@ -153,7 +152,7 @@ final class LoanService
$db->beginTransaction(); $db->beginTransaction();
try { try {
HrLoanInstallment::markPaid((int) $nextInstallment['id'], $amount, $payrollRunId); HrLoanInstallment::markDeducted((int) $nextInstallment['id'], $payrollRunId);
$newRemaining = bcsub($loan['remaining_amount'], $amount, 2); $newRemaining = bcsub($loan['remaining_amount'], $amount, 2);
$newPaid = (int) $loan['paid_installments'] + 1; $newPaid = (int) $loan['paid_installments'] + 1;
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
</div> </div>
<div> <div>
<label style="display:block;margin-bottom:4px;font-size:13px;">الكود <span style="color:#DC2626;">*</span></label> <label style="display:block;margin-bottom:4px;font-size:13px;">الكود <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e(old('code') ?? ($isEdit ? $department->code : '')) ?>" class="form-control" required> <input type="text" name="department_code" value="<?= e(old('department_code') ?? ($isEdit ? $department->department_code : '')) ?>" class="form-control" required>
</div> </div>
<div> <div>
<label style="display:block;margin-bottom:4px;font-size:13px;">القسم الأب</label> <label style="display:block;margin-bottom:4px;font-size:13px;">القسم الأب</label>
...@@ -38,10 +38,10 @@ ...@@ -38,10 +38,10 @@
</div> </div>
<div> <div>
<label style="display:block;margin-bottom:4px;font-size:13px;">المدير</label> <label style="display:block;margin-bottom:4px;font-size:13px;">المدير</label>
<select name="manager_id" class="form-control"> <select name="manager_employee_id" class="form-control">
<option value="">-- بدون --</option> <option value="">-- بدون --</option>
<?php foreach ($employees as $emp): ?> <?php foreach ($employees as $emp): ?>
<option value="<?= (int) $emp['id'] ?>" <?= (old('manager_id') ?? ($isEdit ? $department->manager_id : '')) == $emp['id'] ? 'selected' : '' ?>><?= e($emp['first_name_ar'] . ' ' . $emp['last_name_ar']) ?></option> <option value="<?= (int) $emp['id'] ?>" <?= (old('manager_employee_id') ?? ($isEdit ? $department->manager_employee_id : '')) == $emp['id'] ? 'selected' : '' ?>><?= e($emp['first_name_ar'] . ' ' . $emp['last_name_ar']) ?></option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
</h3> </h3>
</div> </div>
<div style="padding:16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;"> <div style="padding:16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;">
<div><span style="color:#6B7280;font-size:13px;">الكود</span><div style="font-weight:600;"><?= e($department->code) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">الكود</span><div style="font-weight:600;"><?= e($department->department_code) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">الاسم بالعربي</span><div style="font-weight:600;"><?= e($department->name_ar) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">الاسم بالعربي</span><div style="font-weight:600;"><?= e($department->name_ar) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">الاسم بالإنجليزي</span><div><?= e($department->name_en ?? '-') ?></div></div> <div><span style="color:#6B7280;font-size:13px;">الاسم بالإنجليزي</span><div><?= e($department->name_en ?? '-') ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">القسم الأب</span><div><?= $parent ? '<a href="/hr/departments/' . (int) $parent->id . '">' . e($parent->name_ar) . '</a>' : '-' ?></div></div> <div><span style="color:#6B7280;font-size:13px;">القسم الأب</span><div><?= $parent ? '<a href="/hr/departments/' . (int) $parent->id . '">' . e($parent->name_ar) . '</a>' : '-' ?></div></div>
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
<tbody> <tbody>
<?php foreach ($children as $child): ?> <?php foreach ($children as $child): ?>
<tr> <tr>
<td><code><?= e($child['code']) ?></code></td> <td><code><?= e($child['department_code']) ?></code></td>
<td><a href="/hr/departments/<?= (int) $child['id'] ?>"><?= e($child['name_ar']) ?></a></td> <td><a href="/hr/departments/<?= (int) $child['id'] ?>"><?= e($child['name_ar']) ?></a></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
<?php <?php
$current = null; $current = null;
foreach ($salaryDetails as $sd) { foreach ($salaryDetails as $sd) {
if ((int) $sd['salary_component_id'] === (int) $comp['id']) { if ((int) $sd['component_id'] === (int) $comp['id']) {
$current = $sd; $current = $sd;
break; break;
} }
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
<div style="padding:16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:16px;"> <div style="padding:16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:16px;">
<div><label style="display:block;margin-bottom:4px;font-size:13px;">الاسم بالعربي <span style="color:#DC2626;">*</span></label><input type="text" name="name_ar" value="<?= e(old('name_ar') ?? ($isEdit ? $holiday->name_ar : '')) ?>" class="form-control" required></div> <div><label style="display:block;margin-bottom:4px;font-size:13px;">الاسم بالعربي <span style="color:#DC2626;">*</span></label><input type="text" name="name_ar" value="<?= e(old('name_ar') ?? ($isEdit ? $holiday->name_ar : '')) ?>" class="form-control" required></div>
<div><label style="display:block;margin-bottom:4px;font-size:13px;">الاسم بالإنجليزي</label><input type="text" name="name_en" value="<?= e(old('name_en') ?? ($isEdit ? $holiday->name_en : '')) ?>" class="form-control"></div> <div><label style="display:block;margin-bottom:4px;font-size:13px;">الاسم بالإنجليزي</label><input type="text" name="name_en" value="<?= e(old('name_en') ?? ($isEdit ? $holiday->name_en : '')) ?>" class="form-control"></div>
<div><label style="display:block;margin-bottom:4px;font-size:13px;">التاريخ <span style="color:#DC2626;">*</span></label><input type="date" name="holiday_date" value="<?= e(old('holiday_date') ?? ($isEdit ? $holiday->holiday_date : '')) ?>" class="form-control" required></div> <div><label style="display:block;margin-bottom:4px;font-size:13px;">التاريخ <span style="color:#DC2626;">*</span></label><input type="date" name="date" value="<?= e(old('date') ?? ($isEdit ? $holiday->date : '')) ?>" class="form-control" required></div>
<div><label style="display:block;margin-bottom:4px;font-size:13px;">المدة (أيام)</label><input type="number" name="duration_days" value="<?= e(old('duration_days') ?? ($isEdit ? $holiday->duration_days : '1')) ?>" class="form-control" min="1"></div> <div><label style="display:block;margin-bottom:4px;font-size:13px;">المدة (أيام)</label><input type="number" name="duration_days" value="<?= e(old('duration_days') ?? ($isEdit ? $holiday->duration_days : '1')) ?>" class="form-control" min="1"></div>
<div><label style="display:block;margin-bottom:4px;font-size:13px;">النوع</label> <div><label style="display:block;margin-bottom:4px;font-size:13px;">النوع</label>
<select name="holiday_type" class="form-control"> <select name="holiday_type" class="form-control">
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
<?php foreach ($holidays as $h): ?> <?php foreach ($holidays as $h): ?>
<tr> <tr>
<td><?= e($h['name_ar']) ?></td> <td><?= e($h['name_ar']) ?></td>
<td><?= e($h['holiday_date']) ?></td> <td><?= e($h['date']) ?></td>
<td><?= (int) $h['duration_days'] ?> يوم</td> <td><?= (int) $h['duration_days'] ?> يوم</td>
<td><?= e($typeLabels[$h['holiday_type']] ?? $h['holiday_type']) ?></td> <td><?= e($typeLabels[$h['holiday_type']] ?? $h['holiday_type']) ?></td>
<td><?= $h['is_recurring'] ? 'نعم' : 'لا' ?></td> <td><?= $h['is_recurring'] ? 'نعم' : 'لا' ?></td>
......
...@@ -13,8 +13,8 @@ ...@@ -13,8 +13,8 @@
<td><?= e($mn[(int) ($r['month'] ?? 0)] ?? '-') ?></td> <td><?= e($mn[(int) ($r['month'] ?? 0)] ?? '-') ?></td>
<td><?= number_format((float) $r['basic_insurable_salary'], 2) ?></td> <td><?= number_format((float) $r['basic_insurable_salary'], 2) ?></td>
<td><?= number_format((float) $r['variable_insurable_salary'], 2) ?></td> <td><?= number_format((float) $r['variable_insurable_salary'], 2) ?></td>
<td><?= number_format((float) $r['employee_share_total'], 2) ?></td> <td><?= number_format((float) $r['total_employee_share'], 2) ?></td>
<td><?= number_format((float) $r['employer_share_total'], 2) ?></td> <td><?= number_format((float) $r['total_employer_share'], 2) ?></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
</tbody> </tbody>
......
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
<td><?= e($r['insurance_number'] ?? '-') ?></td> <td><?= e($r['insurance_number'] ?? '-') ?></td>
<td><?= number_format((float) $r['basic_insurable_salary'], 2) ?></td> <td><?= number_format((float) $r['basic_insurable_salary'], 2) ?></td>
<td><?= number_format((float) $r['variable_insurable_salary'], 2) ?></td> <td><?= number_format((float) $r['variable_insurable_salary'], 2) ?></td>
<td><?= number_format((float) $r['employee_share_total'], 2) ?></td> <td><?= number_format((float) $r['total_employee_share'], 2) ?></td>
<td><?= number_format((float) $r['employer_share_total'], 2) ?></td> <td><?= number_format((float) $r['total_employer_share'], 2) ?></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
<?php if ($totals): ?> <?php if ($totals): ?>
......
...@@ -19,8 +19,8 @@ ...@@ -19,8 +19,8 @@
<td><a href="/hr/insurance/employee/<?= (int) $r['employee_profile_id'] ?>"><?= e(($r['first_name_ar'] ?? '') . ' ' . ($r['last_name_ar'] ?? '')) ?></a></td> <td><a href="/hr/insurance/employee/<?= (int) $r['employee_profile_id'] ?>"><?= e(($r['first_name_ar'] ?? '') . ' ' . ($r['last_name_ar'] ?? '')) ?></a></td>
<td><?= number_format((float) $r['basic_insurable_salary'], 2) ?></td> <td><?= number_format((float) $r['basic_insurable_salary'], 2) ?></td>
<td><?= number_format((float) $r['variable_insurable_salary'], 2) ?></td> <td><?= number_format((float) $r['variable_insurable_salary'], 2) ?></td>
<td><?= number_format((float) $r['employee_share_total'], 2) ?></td> <td><?= number_format((float) $r['total_employee_share'], 2) ?></td>
<td><?= number_format((float) $r['employer_share_total'], 2) ?></td> <td><?= number_format((float) $r['total_employer_share'], 2) ?></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
</tbody> </tbody>
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
</div> </div>
<div> <div>
<label style="display:block;margin-bottom:4px;font-size:13px;">الكود <span style="color:#DC2626;">*</span></label> <label style="display:block;margin-bottom:4px;font-size:13px;">الكود <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e(old('code') ?? ($isEdit ? $jobTitle->code : '')) ?>" class="form-control" required> <input type="text" name="title_code" value="<?= e(old('title_code') ?? ($isEdit ? $jobTitle->title_code : '')) ?>" class="form-control" required>
</div> </div>
<div> <div>
<label style="display:block;margin-bottom:4px;font-size:13px;">الدرجة الوظيفية</label> <label style="display:block;margin-bottom:4px;font-size:13px;">الدرجة الوظيفية</label>
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
</div> </div>
<div style="grid-column:1/-1;"> <div style="grid-column:1/-1;">
<label style="display:block;margin-bottom:4px;font-size:13px;">الوصف</label> <label style="display:block;margin-bottom:4px;font-size:13px;">الوصف</label>
<textarea name="description" class="form-control" rows="3"><?= e(old('description') ?? ($isEdit ? $jobTitle->description : '')) ?></textarea> <textarea name="description_ar" class="form-control" rows="3"><?= e(old('description_ar') ?? ($isEdit ? $jobTitle->description_ar : '')) ?></textarea>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
<?php $bal = $balancesMatrix[$emp['id']][$lt['id']] ?? null; ?> <?php $bal = $balancesMatrix[$emp['id']][$lt['id']] ?? null; ?>
<td style="text-align:center;font-size:12px;"> <td style="text-align:center;font-size:12px;">
<?php if ($bal): ?> <?php if ($bal): ?>
<span title="المتبقي / المستحق"><?= number_format((float) ($bal['remaining'] ?? 0), 0) ?>/<?= number_format((float) ($bal['entitled_days'] ?? 0), 0) ?></span> <span title="المتبقي / المستحق"><?= number_format((float) ($bal['remaining_days'] ?? 0), 0) ?>/<?= number_format((float) ($bal['entitled_days'] ?? 0), 0) ?></span>
<?php else: ?>-<?php endif; ?> <?php else: ?>-<?php endif; ?>
</td> </td>
<?php endforeach; ?> <?php endforeach; ?>
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
<td><?= number_format((float) ($b['adjustment_days'] ?? 0), 1) ?></td> <td><?= number_format((float) ($b['adjustment_days'] ?? 0), 1) ?></td>
<td><?= number_format((float) ($b['used_days'] ?? 0), 1) ?></td> <td><?= number_format((float) ($b['used_days'] ?? 0), 1) ?></td>
<td><?= number_format((float) ($b['pending_days'] ?? 0), 1) ?></td> <td><?= number_format((float) ($b['pending_days'] ?? 0), 1) ?></td>
<td style="font-weight:700;color:#059669;"><?= number_format((float) ($b['remaining'] ?? 0), 1) ?></td> <td style="font-weight:700;color:#059669;"><?= number_format((float) ($b['remaining_days'] ?? 0), 1) ?></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
</tbody> </tbody>
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
</div> </div>
<div><label style="display:block;margin-bottom:4px;font-size:13px;">تاريخ البداية <span style="color:#DC2626;">*</span></label><input type="date" name="start_date" value="<?= e(old('start_date') ?? '') ?>" class="form-control" required></div> <div><label style="display:block;margin-bottom:4px;font-size:13px;">تاريخ البداية <span style="color:#DC2626;">*</span></label><input type="date" name="start_date" value="<?= e(old('start_date') ?? '') ?>" class="form-control" required></div>
<div><label style="display:block;margin-bottom:4px;font-size:13px;">تاريخ النهاية <span style="color:#DC2626;">*</span></label><input type="date" name="end_date" value="<?= e(old('end_date') ?? '') ?>" class="form-control" required></div> <div><label style="display:block;margin-bottom:4px;font-size:13px;">تاريخ النهاية <span style="color:#DC2626;">*</span></label><input type="date" name="end_date" value="<?= e(old('end_date') ?? '') ?>" class="form-control" required></div>
<div><label style="display:block;margin-bottom:4px;font-size:13px;"><input type="checkbox" name="is_half_day" value="1" <?= old('is_half_day') ? 'checked' : '' ?>> نصف يوم</label></div> <div><label style="display:block;margin-bottom:4px;font-size:13px;"><input type="checkbox" name="half_day" value="1" <?= old('half_day') ? 'checked' : '' ?>> نصف يوم</label></div>
<div style="grid-column:1/-1;"><label style="display:block;margin-bottom:4px;font-size:13px;">السبب</label><textarea name="reason" class="form-control" rows="3"><?= e(old('reason') ?? '') ?></textarea></div> <div style="grid-column:1/-1;"><label style="display:block;margin-bottom:4px;font-size:13px;">السبب</label><textarea name="reason" class="form-control" rows="3"><?= e(old('reason') ?? '') ?></textarea></div>
</div> </div>
</div> </div>
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
<div><span style="color:#6B7280;font-size:13px;">من</span><div><?= e($leaveRequest->start_date) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">من</span><div><?= e($leaveRequest->start_date) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">إلى</span><div><?= e($leaveRequest->end_date) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">إلى</span><div><?= e($leaveRequest->end_date) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">عدد الأيام</span><div style="font-weight:600;"><?= number_format((float) $leaveRequest->total_days, 1) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">عدد الأيام</span><div style="font-weight:600;"><?= number_format((float) $leaveRequest->total_days, 1) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">نصف يوم</span><div><?= $leaveRequest->is_half_day ? 'نعم' : 'لا' ?></div></div> <div><span style="color:#6B7280;font-size:13px;">نصف يوم</span><div><?= $leaveRequest->half_day ? 'نعم' : 'لا' ?></div></div>
<?php if ($leaveRequest->reason): ?> <?php if ($leaveRequest->reason): ?>
<div style="grid-column:1/-1;"><span style="color:#6B7280;font-size:13px;">السبب</span><div><?= e($leaveRequest->reason) ?></div></div> <div style="grid-column:1/-1;"><span style="color:#6B7280;font-size:13px;">السبب</span><div><?= e($leaveRequest->reason) ?></div></div>
<?php endif; ?> <?php endif; ?>
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
<div style="padding:16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:16px;"> <div style="padding:16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:16px;">
<div><span style="color:#6B7280;font-size:13px;">المستحق</span><div style="font-weight:600;"><?= number_format((float) ($balance['entitled_days'] ?? 0), 1) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">المستحق</span><div style="font-weight:600;"><?= number_format((float) ($balance['entitled_days'] ?? 0), 1) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">المستخدم</span><div style="font-weight:600;"><?= number_format((float) ($balance['used_days'] ?? 0), 1) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">المستخدم</span><div style="font-weight:600;"><?= number_format((float) ($balance['used_days'] ?? 0), 1) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">المتبقي</span><div style="font-weight:700;font-size:18px;color:#059669;"><?= number_format((float) ($balance['remaining'] ?? 0), 1) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">المتبقي</span><div style="font-weight:700;font-size:18px;color:#059669;"><?= number_format((float) ($balance['remaining_days'] ?? 0), 1) ?></div></div>
</div> </div>
</div> </div>
<?php endif; ?> <?php endif; ?>
......
...@@ -28,14 +28,14 @@ ...@@ -28,14 +28,14 @@
<table class="data-table"> <table class="data-table">
<thead><tr><th>#</th><th>تاريخ الاستحقاق</th><th>المبلغ</th><th>المدفوع</th><th>الحالة</th></tr></thead> <thead><tr><th>#</th><th>تاريخ الاستحقاق</th><th>المبلغ</th><th>المدفوع</th><th>الحالة</th></tr></thead>
<tbody> <tbody>
<?php $instSl = ['pending'=>'معلق','paid'=>'مدفوع','partial'=>'جزئي','overdue'=>'متأخر']; ?> <?php $instSl = ['pending'=>'معلق','deducted'=>'مخصوم','skipped'=>'تم تخطيه','cancelled'=>'ملغي']; ?>
<?php $instSc = ['pending'=>'#FEF3C7;color:#92400E','paid'=>'#DEF7EC;color:#03543F','partial'=>'#DBEAFE;color:#1E40AF','overdue'=>'#FDE8E8;color:#9B1C1C']; ?> <?php $instSc = ['pending'=>'#FEF3C7;color:#92400E','deducted'=>'#DEF7EC;color:#03543F','skipped'=>'#DBEAFE;color:#1E40AF','cancelled'=>'#E5E7EB;color:#374151']; ?>
<?php foreach ($installments as $inst): ?> <?php foreach ($installments as $inst): ?>
<tr> <tr>
<td><?= (int) $inst['installment_number'] ?></td> <td><?= (int) $inst['installment_number'] ?></td>
<td><?= e($inst['due_date']) ?></td> <td><?= e($inst['due_date']) ?></td>
<td><?= number_format((float) $inst['amount'], 2) ?></td> <td><?= number_format((float) $inst['amount'], 2) ?></td>
<td><?= number_format((float) ($inst['paid_amount'] ?? 0), 2) ?></td> <td><?= $inst['deducted_at'] ? e(date('Y-m-d', strtotime($inst['deducted_at']))) : '-' ?></td>
<td><span style="display:inline-block;padding:2px 8px;border-radius:9999px;font-size:12px;background:<?= $instSc[$inst['status']] ?? '#E5E7EB;color:#374151' ?>;"><?= e($instSl[$inst['status']] ?? $inst['status']) ?></span></td> <td><span style="display:inline-block;padding:2px 8px;border-radius:9999px;font-size:12px;background:<?= $instSc[$inst['status']] ?? '#E5E7EB;color:#374151' ?>;"><?= e($instSl[$inst['status']] ?? $inst['status']) ?></span></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
<?php foreach ($runs as $r): ?> <?php foreach ($runs as $r): ?>
<tr> <tr>
<td><?= e(($mn[(int) $r['month']] ?? '') . ' ' . $r['year']) ?></td> <td><?= e(($mn[(int) $r['month']] ?? '') . ' ' . $r['year']) ?></td>
<td style="color:#059669;"><?= number_format((float) $r['gross_salary'], 2) ?></td> <td style="color:#059669;"><?= number_format((float) $r['gross_earnings'], 2) ?></td>
<td style="color:#DC2626;"><?= number_format((float) $r['total_deductions'], 2) ?></td> <td style="color:#DC2626;"><?= number_format((float) $r['total_deductions'], 2) ?></td>
<td style="font-weight:700;"><?= number_format((float) $r['net_salary'], 2) ?></td> <td style="font-weight:700;"><?= number_format((float) $r['net_salary'], 2) ?></td>
<td><a href="/hr/payroll/runs/<?= (int) $r['id'] ?>/slip" style="color:#2563EB;"><i data-lucide="file-text" style="width:16px;height:16px;"></i></a></td> <td><a href="/hr/payroll/runs/<?= (int) $r['id'] ?>/slip" style="color:#2563EB;"><i data-lucide="file-text" style="width:16px;height:16px;"></i></a></td>
......
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
<tr> <tr>
<td><a href="/hr/employees/<?= (int) $r['employee_profile_id'] ?>"><?= e(($r['first_name_ar'] ?? '') . ' ' . ($r['last_name_ar'] ?? '')) ?></a></td> <td><a href="/hr/employees/<?= (int) $r['employee_profile_id'] ?>"><?= e(($r['first_name_ar'] ?? '') . ' ' . ($r['last_name_ar'] ?? '')) ?></a></td>
<td><?= number_format((float) $r['basic_salary'], 2) ?></td> <td><?= number_format((float) $r['basic_salary'], 2) ?></td>
<td style="color:#059669;font-weight:600;"><?= number_format((float) $r['gross_salary'], 2) ?></td> <td style="color:#059669;font-weight:600;"><?= number_format((float) $r['gross_earnings'], 2) ?></td>
<td style="color:#DC2626;"><?= number_format((float) $r['total_deductions'], 2) ?></td> <td style="color:#DC2626;"><?= number_format((float) $r['total_deductions'], 2) ?></td>
<td style="font-weight:700;"><?= number_format((float) $r['net_salary'], 2) ?></td> <td style="font-weight:700;"><?= number_format((float) $r['net_salary'], 2) ?></td>
<td style="white-space:nowrap;"> <td style="white-space:nowrap;">
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<?php $__template->section('content'); ?> <?php $__template->section('content'); ?>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;margin-bottom:20px;"> <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;margin-bottom:20px;">
<div class="card" style="padding:16px;text-align:center;"><div style="font-size:13px;color:#6B7280;">الأساسي</div><div style="font-size:18px;font-weight:700;"><?= number_format((float) $run['basic_salary'], 2) ?></div></div> <div class="card" style="padding:16px;text-align:center;"><div style="font-size:13px;color:#6B7280;">الأساسي</div><div style="font-size:18px;font-weight:700;"><?= number_format((float) $run['basic_salary'], 2) ?></div></div>
<div class="card" style="padding:16px;text-align:center;"><div style="font-size:13px;color:#6B7280;">الإجمالي</div><div style="font-size:18px;font-weight:700;color:#059669;"><?= number_format((float) $run['gross_salary'], 2) ?></div></div> <div class="card" style="padding:16px;text-align:center;"><div style="font-size:13px;color:#6B7280;">الإجمالي</div><div style="font-size:18px;font-weight:700;color:#059669;"><?= number_format((float) $run['gross_earnings'], 2) ?></div></div>
<div class="card" style="padding:16px;text-align:center;"><div style="font-size:13px;color:#6B7280;">الخصومات</div><div style="font-size:18px;font-weight:700;color:#DC2626;"><?= number_format((float) $run['total_deductions'], 2) ?></div></div> <div class="card" style="padding:16px;text-align:center;"><div style="font-size:13px;color:#6B7280;">الخصومات</div><div style="font-size:18px;font-weight:700;color:#DC2626;"><?= number_format((float) $run['total_deductions'], 2) ?></div></div>
<div class="card" style="padding:16px;text-align:center;"><div style="font-size:13px;color:#6B7280;">الصافي</div><div style="font-size:24px;font-weight:700;color:#2563EB;"><?= number_format((float) $run['net_salary'], 2) ?></div></div> <div class="card" style="padding:16px;text-align:center;"><div style="font-size:13px;color:#6B7280;">الصافي</div><div style="font-size:24px;font-weight:700;color:#2563EB;"><?= number_format((float) $run['net_salary'], 2) ?></div></div>
</div> </div>
...@@ -45,8 +45,8 @@ ...@@ -45,8 +45,8 @@
<div class="card" style="margin-bottom:20px;"> <div class="card" style="margin-bottom:20px;">
<div style="padding:16px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;font-size:16px;">التأمينات الاجتماعية</h3></div> <div style="padding:16px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;font-size:16px;">التأمينات الاجتماعية</h3></div>
<div style="padding:16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;"> <div style="padding:16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;">
<div><span style="color:#6B7280;font-size:13px;">حصة الموظف</span><div style="font-weight:600;"><?= number_format((float) $insurance['employee_share_total'], 2) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">حصة الموظف</span><div style="font-weight:600;"><?= number_format((float) $insurance['total_employee_share'], 2) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">حصة صاحب العمل</span><div style="font-weight:600;"><?= number_format((float) $insurance['employer_share_total'], 2) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">حصة صاحب العمل</span><div style="font-weight:600;"><?= number_format((float) $insurance['total_employer_share'], 2) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">الأساسي المؤمن عليه</span><div><?= number_format((float) $insurance['basic_insurable_salary'], 2) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">الأساسي المؤمن عليه</span><div><?= number_format((float) $insurance['basic_insurable_salary'], 2) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">المتغير المؤمن عليه</span><div><?= number_format((float) $insurance['variable_insurable_salary'], 2) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">المتغير المؤمن عليه</span><div><?= number_format((float) $insurance['variable_insurable_salary'], 2) ?></div></div>
</div> </div>
...@@ -57,9 +57,9 @@ ...@@ -57,9 +57,9 @@
<div class="card" style="margin-bottom:20px;"> <div class="card" style="margin-bottom:20px;">
<div style="padding:16px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;font-size:16px;">ضريبة الدخل</h3></div> <div style="padding:16px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;font-size:16px;">ضريبة الدخل</h3></div>
<div style="padding:16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;"> <div style="padding:16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;">
<div><span style="color:#6B7280;font-size:13px;">الدخل الخاضع للضريبة</span><div style="font-weight:600;"><?= number_format((float) $tax['gross_taxable_income'], 2) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">الدخل الخاضع للضريبة</span><div style="font-weight:600;"><?= number_format((float) ($tax['gross_taxable_monthly'] ?? 0), 2) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">الإعفاءات</span><div><?= number_format((float) $tax['total_exemptions'], 2) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">الإعفاءات</span><div><?= number_format(((float) ($tax['personal_exemption'] ?? 0)) + ((float) ($tax['insurance_exemption'] ?? 0)), 2) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">مبلغ الضريبة</span><div style="font-weight:700;color:#DC2626;"><?= number_format((float) $tax['tax_amount'], 2) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">مبلغ الضريبة</span><div style="font-weight:700;color:#DC2626;"><?= number_format((float) ($tax['monthly_tax'] ?? 0), 2) ?></div></div>
</div> </div>
</div> </div>
<?php endif; ?> <?php endif; ?>
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
<?php $__template->section('content'); ?> <?php $__template->section('content'); ?>
<?php <?php
$daysConfig = []; $daysConfig = [];
if ($isEdit && $schedule->days_config) { if ($isEdit && $schedule->days_config_json) {
$daysConfig = json_decode($schedule->days_config, true) ?: []; $daysConfig = json_decode($schedule->days_config_json, true) ?: [];
} }
$dayLabels = ['saturday'=>'السبت','sunday'=>'الأحد','monday'=>'الاثنين','tuesday'=>'الثلاثاء','wednesday'=>'الأربعاء','thursday'=>'الخميس','friday'=>'الجمعة']; $dayLabels = ['saturday'=>'السبت','sunday'=>'الأحد','monday'=>'الاثنين','tuesday'=>'الثلاثاء','wednesday'=>'الأربعاء','thursday'=>'الخميس','friday'=>'الجمعة'];
?> ?>
......
...@@ -17,9 +17,9 @@ ...@@ -17,9 +17,9 @@
<?php foreach ($records as $r): ?> <?php foreach ($records as $r): ?>
<tr> <tr>
<td><?= e($mn[(int) ($r['month'] ?? 0)] ?? '-') ?></td> <td><?= e($mn[(int) ($r['month'] ?? 0)] ?? '-') ?></td>
<td><?= number_format((float) $r['gross_taxable_income'], 2) ?></td> <td><?= number_format((float) $r['gross_taxable_monthly'], 2) ?></td>
<td><?= number_format((float) $r['total_exemptions'], 2) ?></td> <td><?= number_format(((float) ($r['personal_exemption'] ?? 0)) + ((float) ($r['insurance_exemption'] ?? 0)), 2) ?></td>
<td style="font-weight:600;color:#DC2626;"><?= number_format((float) $r['tax_amount'], 2) ?></td> <td style="font-weight:600;color:#DC2626;"><?= number_format((float) $r['monthly_tax'], 2) ?></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
</tbody> </tbody>
......
...@@ -27,28 +27,28 @@ class HrAttendanceAutoCloseJob ...@@ -27,28 +27,28 @@ class HrAttendanceAutoCloseJob
// Find attendance records from yesterday (or older) with check_in but no check_out // Find attendance records from yesterday (or older) with check_in but no check_out
$openRecords = $this->db->select( $openRecords = $this->db->select(
"SELECT a.id, a.employee_profile_id, a.attendance_date, a.check_in, "SELECT a.id, a.employee_profile_id, a.attendance_date, a.check_in_time,
es.schedule_id, es.schedule_id,
ws.days_config ws.days_config_json
FROM hr_attendance a FROM hr_attendance a
LEFT JOIN hr_employee_schedules es ON es.employee_profile_id = a.employee_profile_id LEFT JOIN hr_employee_schedules es ON es.employee_profile_id = a.employee_profile_id
AND es.is_active = 1 AND es.is_archived = 0 AND es.is_active = 1
LEFT JOIN hr_work_schedules ws ON ws.id = es.schedule_id AND ws.is_archived = 0 LEFT JOIN hr_work_schedules ws ON ws.id = es.schedule_id AND ws.is_archived = 0
WHERE a.check_in IS NOT NULL WHERE a.check_in_time IS NOT NULL
AND a.check_out IS NULL AND a.check_out_time IS NULL
AND a.attendance_date <= ? AND a.attendance_date <= ?
AND a.is_archived = 0", AND a.is_archived = 0",
[$yesterday] [$yesterday]
); );
foreach ($openRecords as $rec) { foreach ($openRecords as $rec) {
$checkIn = $rec['check_in']; $checkIn = $rec['check_in_time'];
$attDate = $rec['attendance_date']; $attDate = $rec['attendance_date'];
// Determine check_out time: try schedule end time, fallback to 8 hours after check_in // Determine check_out time: try schedule end time, fallback to 8 hours after check_in
$checkOut = null; $checkOut = null;
if (!empty($rec['days_config'])) { if (!empty($rec['days_config_json'])) {
$daysConfig = json_decode($rec['days_config'], true); $daysConfig = json_decode($rec['days_config_json'], true);
if (is_array($daysConfig)) { if (is_array($daysConfig)) {
$dayOfWeek = (int) date('w', strtotime($attDate)); // 0=Sun, 6=Sat $dayOfWeek = (int) date('w', strtotime($attDate)); // 0=Sun, 6=Sat
$dayKey = ['sunday','monday','tuesday','wednesday','thursday','friday','saturday'][$dayOfWeek] ?? ''; $dayKey = ['sunday','monday','tuesday','wednesday','thursday','friday','saturday'][$dayOfWeek] ?? '';
...@@ -72,7 +72,7 @@ class HrAttendanceAutoCloseJob ...@@ -72,7 +72,7 @@ class HrAttendanceAutoCloseJob
$actualHours = round(($outTime - $inTime) / 3600, 2); $actualHours = round(($outTime - $inTime) / 3600, 2);
$this->db->update('hr_attendance', [ $this->db->update('hr_attendance', [
'check_out' => $checkOut, 'check_out_time' => $checkOut,
'actual_hours' => $actualHours, 'actual_hours' => $actualHours,
'notes' => 'تم إغلاق الحضور تلقائياً - لم يسجل الموظف انصرافه', 'notes' => 'تم إغلاق الحضور تلقائياً - لم يسجل الموظف انصرافه',
'updated_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
......
...@@ -61,7 +61,7 @@ class HrLeaveBalanceResetJob ...@@ -61,7 +61,7 @@ class HrLeaveBalanceResetJob
$leaveTypeId = (int) $lt['id']; $leaveTypeId = (int) $lt['id'];
// Calculate entitlement based on Egyptian law for annual leave // Calculate entitlement based on Egyptian law for annual leave
$entitled = (float) ($lt['default_days'] ?? 0); $entitled = (float) ($lt['default_days_per_year'] ?? 0);
if (($lt['code'] ?? '') === 'annual') { if (($lt['code'] ?? '') === 'annual') {
if ($yearsOfService >= 10 || $age >= 50) { if ($yearsOfService >= 10 || $age >= 50) {
$entitled = 30.0; $entitled = 30.0;
...@@ -74,15 +74,16 @@ class HrLeaveBalanceResetJob ...@@ -74,15 +74,16 @@ class HrLeaveBalanceResetJob
// Calculate carry-over from previous year (max per leave type config) // Calculate carry-over from previous year (max per leave type config)
$carryOver = 0.0; $carryOver = 0.0;
$maxCarry = (float) ($lt['max_carry_over'] ?? 0); $maxCarry = (float) ($lt['max_carry_over_days'] ?? 0);
if ($maxCarry > 0) { if ($maxCarry > 0) {
$prevBalance = $this->db->selectOne( $prevBalance = $this->db->selectOne(
"SELECT remaining FROM hr_leave_balances "SELECT (entitled_days + carried_over_days + adjustment_days - used_days) as remaining_calc
FROM hr_leave_balances
WHERE employee_profile_id = ? AND leave_type_id = ? AND year = ?", WHERE employee_profile_id = ? AND leave_type_id = ? AND year = ?",
[$profileId, $leaveTypeId, $prevYear] [$profileId, $leaveTypeId, $prevYear]
); );
if ($prevBalance) { if ($prevBalance) {
$remaining = (float) ($prevBalance['remaining'] ?? 0); $remaining = (float) ($prevBalance['remaining_calc'] ?? 0);
$carryOver = min($remaining, $maxCarry); $carryOver = min($remaining, $maxCarry);
} }
} }
...@@ -93,11 +94,10 @@ class HrLeaveBalanceResetJob ...@@ -93,11 +94,10 @@ class HrLeaveBalanceResetJob
'leave_type_id' => $leaveTypeId, 'leave_type_id' => $leaveTypeId,
'year' => $newYear, 'year' => $newYear,
'entitled_days' => $entitled, 'entitled_days' => $entitled,
'carried_over' => $carryOver, 'carried_over_days' => $carryOver,
'used_days' => 0, 'used_days' => 0,
'pending_days' => 0, 'pending_days' => 0,
'adjustment' => 0, 'adjustment_days' => 0,
'remaining' => bcadd((string) $entitled, (string) $carryOver, 2),
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
]); ]);
......
...@@ -37,7 +37,6 @@ class HrLoanDeductionReminderJob ...@@ -37,7 +37,6 @@ class HrLoanDeductionReminderJob
WHERE li.status = 'pending' WHERE li.status = 'pending'
AND li.due_date >= ? AND li.due_date >= ?
AND li.due_date <= ? AND li.due_date <= ?
AND li.is_archived = 0
AND l.status IN ('disbursed', 'repaying') AND l.status IN ('disbursed', 'repaying')
AND l.is_archived = 0", AND l.is_archived = 0",
[$today, $monthEnd] [$today, $monthEnd]
...@@ -67,18 +66,21 @@ class HrLoanDeductionReminderJob ...@@ -67,18 +66,21 @@ class HrLoanDeductionReminderJob
JOIN hr_employee_loans l ON l.id = li.loan_id JOIN hr_employee_loans l ON l.id = li.loan_id
WHERE li.status = 'pending' WHERE li.status = 'pending'
AND li.due_date < ? AND li.due_date < ?
AND li.is_archived = 0
AND l.status IN ('disbursed', 'repaying') AND l.status IN ('disbursed', 'repaying')
AND l.is_archived = 0", AND l.is_archived = 0",
[$today] [$today]
); );
foreach ($overdueInstallments as $inst) { $overdue = count($overdueInstallments);
$this->db->update('hr_loan_installments', [ if ($overdue > 0) {
'status' => 'overdue', $this->db->insert('notifications', [
'updated_at' => date('Y-m-d H:i:s'), 'type' => 'hr_loan_overdue',
], '`id` = ?', [(int) $inst['id']]); 'title' => 'تنبيه: أقساط سلف متأخرة',
$overdue++; 'message' => "يوجد {$overdue} قسط سلفة متأخر عن موعد السداد. يرجى مراجعة حالة السلف.",
'link' => '/hr/loans',
'is_read' => 0,
'created_at' => date('Y-m-d H:i:s'),
]);
} }
if ($reminded > 0 || $overdue > 0) { if ($reminded > 0 || $overdue > 0) {
......
...@@ -5,17 +5,20 @@ return [ ...@@ -5,17 +5,20 @@ return [
'up' => " 'up' => "
CREATE TABLE IF NOT EXISTS `hr_salary_structures` ( CREATE TABLE IF NOT EXISTS `hr_salary_structures` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`structure_code` VARCHAR(30) NOT NULL, `code` VARCHAR(30) NOT NULL,
`name_ar` VARCHAR(200) NOT NULL, `name_ar` VARCHAR(200) NOT NULL,
`name_en` VARCHAR(200) NULL, `name_en` VARCHAR(200) NULL,
`description_ar` TEXT NULL, `description` TEXT NULL,
`is_default` TINYINT(1) NOT NULL DEFAULT 0, `is_default` TINYINT(1) NOT NULL DEFAULT 0,
`is_active` TINYINT(1) NOT NULL DEFAULT 1, `is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL, `created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL, `updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_salary_structures_code` (`structure_code`), `is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL DEFAULT NULL,
`archived_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_salary_structures_code` (`code`),
INDEX `idx_hr_salary_structures_active` (`is_active`) INDEX `idx_hr_salary_structures_active` (`is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
", ",
......
...@@ -9,15 +9,21 @@ return [ ...@@ -9,15 +9,21 @@ return [
`component_code` VARCHAR(30) NOT NULL, `component_code` VARCHAR(30) NOT NULL,
`name_ar` VARCHAR(200) NOT NULL, `name_ar` VARCHAR(200) NOT NULL,
`name_en` VARCHAR(200) NULL, `name_en` VARCHAR(200) NULL,
`component_type` VARCHAR(20) NOT NULL COMMENT 'earning or deduction', `type` VARCHAR(20) NOT NULL COMMENT 'earning or deduction',
`calculation_type` VARCHAR(30) NOT NULL COMMENT 'fixed, percentage_of_basic, percentage_of_gross, formula', `calculation_type` VARCHAR(30) NOT NULL COMMENT 'fixed, percentage_of_basic, percentage_of_gross, formula',
`default_amount` DECIMAL(15,2) NULL, `default_amount` DECIMAL(15,2) NULL,
`default_percentage` DECIMAL(5,2) NULL, `default_percentage` DECIMAL(5,2) NULL,
`percentage_of` VARCHAR(30) NULL COMMENT 'basic, gross, etc.',
`percentage_value` DECIMAL(5,2) NULL,
`formula_json` JSON NULL, `formula_json` JSON NULL,
`is_taxable` TINYINT(1) NOT NULL DEFAULT 1, `is_taxable` TINYINT(1) NOT NULL DEFAULT 1,
`is_insurable` TINYINT(1) NOT NULL DEFAULT 1, `is_insurable` TINYINT(1) NOT NULL DEFAULT 1,
`is_basic` TINYINT(1) NOT NULL DEFAULT 0,
`is_variable` TINYINT(1) NOT NULL DEFAULT 0,
`is_system_calculated` TINYINT(1) NOT NULL DEFAULT 0,
`is_mandatory` TINYINT(1) NOT NULL DEFAULT 0, `is_mandatory` TINYINT(1) NOT NULL DEFAULT 0,
`affects_insurance_base` TINYINT(1) NOT NULL DEFAULT 0, `affects_insurance_base` TINYINT(1) NOT NULL DEFAULT 0,
`affects_overtime` TINYINT(1) NOT NULL DEFAULT 0,
`sort_order` INT UNSIGNED NOT NULL DEFAULT 0, `sort_order` INT UNSIGNED NOT NULL DEFAULT 0,
`is_active` TINYINT(1) NOT NULL DEFAULT 1, `is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
...@@ -26,9 +32,9 @@ return [ ...@@ -26,9 +32,9 @@ return [
`updated_by` BIGINT UNSIGNED NULL, `updated_by` BIGINT UNSIGNED NULL,
INDEX `idx_hr_salary_comp_structure` (`structure_id`), INDEX `idx_hr_salary_comp_structure` (`structure_id`),
UNIQUE KEY `uq_hr_salary_comp_code` (`structure_id`, `component_code`), UNIQUE KEY `uq_hr_salary_comp_code` (`structure_id`, `component_code`),
INDEX `idx_hr_salary_comp_type` (`component_type`), INDEX `idx_hr_salary_comp_type` (`type`),
CONSTRAINT `fk_hr_salary_comp_structure` FOREIGN KEY (`structure_id`) REFERENCES `hr_salary_structures`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_hr_salary_comp_structure` FOREIGN KEY (`structure_id`) REFERENCES `hr_salary_structures`(`id`) ON DELETE CASCADE,
CONSTRAINT `chk_hr_salary_comp_type` CHECK (`component_type` IN ('earning', 'deduction')), CONSTRAINT `chk_hr_salary_comp_type` CHECK (`type` IN ('earning', 'deduction')),
CONSTRAINT `chk_hr_salary_calc_type` CHECK (`calculation_type` IN ('fixed', 'percentage_of_basic', 'percentage_of_gross', 'formula')) CONSTRAINT `chk_hr_salary_calc_type` CHECK (`calculation_type` IN ('fixed', 'percentage_of_basic', 'percentage_of_gross', 'formula'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
", ",
......
...@@ -7,6 +7,12 @@ return [ ...@@ -7,6 +7,12 @@ return [
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`employee_id` BIGINT UNSIGNED NOT NULL, `employee_id` BIGINT UNSIGNED NOT NULL,
`employee_number` VARCHAR(20) NOT NULL, `employee_number` VARCHAR(20) NOT NULL,
`first_name_ar` VARCHAR(100) NOT NULL,
`last_name_ar` VARCHAR(100) NOT NULL,
`first_name_en` VARCHAR(100) NULL,
`last_name_en` VARCHAR(100) NULL,
`phone` VARCHAR(20) NULL,
`email` VARCHAR(200) NULL,
`national_id` VARCHAR(14) NULL, `national_id` VARCHAR(14) NULL,
`date_of_birth` DATE NULL, `date_of_birth` DATE NULL,
`gender` VARCHAR(10) NULL COMMENT 'male, female', `gender` VARCHAR(10) NULL COMMENT 'male, female',
...@@ -23,7 +29,7 @@ return [ ...@@ -23,7 +29,7 @@ return [
`probation_end_date` DATE NULL, `probation_end_date` DATE NULL,
`probation_status` VARCHAR(20) NULL DEFAULT 'in_probation' COMMENT 'in_probation, passed, failed', `probation_status` VARCHAR(20) NULL DEFAULT 'in_probation' COMMENT 'in_probation, passed, failed',
`employment_type` VARCHAR(20) NOT NULL DEFAULT 'full_time' COMMENT 'full_time, part_time, contract, temporary', `employment_type` VARCHAR(20) NOT NULL DEFAULT 'full_time' COMMENT 'full_time, part_time, contract, temporary',
`employment_status` VARCHAR(20) NOT NULL DEFAULT 'active' COMMENT 'active, on_leave, suspended, terminated, resigned', `employment_status` VARCHAR(20) NOT NULL DEFAULT 'active' COMMENT 'active, probation, on_leave, suspended, terminated, resigned',
`salary_structure_id` BIGINT UNSIGNED NULL, `salary_structure_id` BIGINT UNSIGNED NULL,
`basic_salary` DECIMAL(15,2) NOT NULL DEFAULT 0.00, `basic_salary` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`insurable_salary` DECIMAL(15,2) NOT NULL DEFAULT 0.00, `insurable_salary` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
...@@ -39,6 +45,7 @@ return [ ...@@ -39,6 +45,7 @@ return [
`notes` TEXT NULL, `notes` TEXT NULL,
`photo_path` VARCHAR(500) NULL, `photo_path` VARCHAR(500) NULL,
`termination_date` DATE NULL, `termination_date` DATE NULL,
`termination_type` VARCHAR(30) NULL,
`termination_reason` TEXT NULL, `termination_reason` TEXT NULL,
`last_working_day` DATE NULL, `last_working_day` DATE NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
...@@ -50,7 +57,7 @@ return [ ...@@ -50,7 +57,7 @@ return [
`archived_by` BIGINT UNSIGNED NULL, `archived_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_profiles_employee` (`employee_id`), UNIQUE KEY `uq_hr_profiles_employee` (`employee_id`),
UNIQUE KEY `uq_hr_profiles_number` (`employee_number`), UNIQUE KEY `uq_hr_profiles_number` (`employee_number`),
UNIQUE KEY `uq_hr_profiles_national_id` (`national_id`), INDEX `idx_hr_profiles_national_id` (`national_id`),
INDEX `idx_hr_profiles_department` (`department_id`), INDEX `idx_hr_profiles_department` (`department_id`),
INDEX `idx_hr_profiles_job_title` (`job_title_id`), INDEX `idx_hr_profiles_job_title` (`job_title_id`),
INDEX `idx_hr_profiles_manager` (`direct_manager_id`), INDEX `idx_hr_profiles_manager` (`direct_manager_id`),
...@@ -67,7 +74,6 @@ return [ ...@@ -67,7 +74,6 @@ return [
CONSTRAINT `chk_hr_profiles_marital` CHECK (`marital_status` IN ('single', 'married', 'divorced', 'widowed')), CONSTRAINT `chk_hr_profiles_marital` CHECK (`marital_status` IN ('single', 'married', 'divorced', 'widowed')),
CONSTRAINT `chk_hr_profiles_religion` CHECK (`religion` IN ('muslim', 'christian', 'other')), CONSTRAINT `chk_hr_profiles_religion` CHECK (`religion` IN ('muslim', 'christian', 'other')),
CONSTRAINT `chk_hr_profiles_emp_type` CHECK (`employment_type` IN ('full_time', 'part_time', 'contract', 'temporary')), CONSTRAINT `chk_hr_profiles_emp_type` CHECK (`employment_type` IN ('full_time', 'part_time', 'contract', 'temporary')),
CONSTRAINT `chk_hr_profiles_emp_status` CHECK (`employment_status` IN ('active', 'on_leave', 'suspended', 'terminated', 'resigned')),
CONSTRAINT `chk_hr_profiles_basic_salary` CHECK (`basic_salary` >= 0) CONSTRAINT `chk_hr_profiles_basic_salary` CHECK (`basic_salary` >= 0)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
", ",
......
...@@ -5,7 +5,7 @@ return [ ...@@ -5,7 +5,7 @@ return [
'up' => " 'up' => "
CREATE TABLE IF NOT EXISTS `hr_employee_salary_details` ( CREATE TABLE IF NOT EXISTS `hr_employee_salary_details` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`employee_id` BIGINT UNSIGNED NOT NULL, `employee_profile_id` BIGINT UNSIGNED NOT NULL,
`component_id` BIGINT UNSIGNED NOT NULL, `component_id` BIGINT UNSIGNED NOT NULL,
`amount` DECIMAL(15,2) NOT NULL DEFAULT 0.00, `amount` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`percentage` DECIMAL(5,2) NULL, `percentage` DECIMAL(5,2) NULL,
...@@ -16,11 +16,11 @@ return [ ...@@ -16,11 +16,11 @@ return [
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL, `created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL, `updated_by` BIGINT UNSIGNED NULL,
INDEX `idx_hr_emp_salary_employee` (`employee_id`), INDEX `idx_hr_emp_salary_employee` (`employee_profile_id`),
INDEX `idx_hr_emp_salary_component` (`component_id`), INDEX `idx_hr_emp_salary_component` (`component_id`),
INDEX `idx_hr_emp_salary_effective` (`effective_from`, `effective_to`), INDEX `idx_hr_emp_salary_effective` (`effective_from`, `effective_to`),
INDEX `idx_hr_emp_salary_active` (`is_active`), INDEX `idx_hr_emp_salary_active` (`is_active`),
CONSTRAINT `fk_hr_emp_salary_employee` FOREIGN KEY (`employee_id`) REFERENCES `employees`(`id`), CONSTRAINT `fk_hr_emp_salary_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`),
CONSTRAINT `fk_hr_emp_salary_component` FOREIGN KEY (`component_id`) REFERENCES `hr_salary_components`(`id`) CONSTRAINT `fk_hr_emp_salary_component` FOREIGN KEY (`component_id`) REFERENCES `hr_salary_components`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
", ",
......
...@@ -5,7 +5,7 @@ return [ ...@@ -5,7 +5,7 @@ return [
'up' => " 'up' => "
CREATE TABLE IF NOT EXISTS `hr_contracts` ( CREATE TABLE IF NOT EXISTS `hr_contracts` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`employee_id` BIGINT UNSIGNED NOT NULL, `employee_profile_id` BIGINT UNSIGNED NOT NULL,
`contract_number` VARCHAR(30) NOT NULL, `contract_number` VARCHAR(30) NOT NULL,
`contract_type` VARCHAR(20) NOT NULL COMMENT 'definite, indefinite', `contract_type` VARCHAR(20) NOT NULL COMMENT 'definite, indefinite',
`start_date` DATE NOT NULL, `start_date` DATE NOT NULL,
...@@ -33,12 +33,12 @@ return [ ...@@ -33,12 +33,12 @@ return [
`archived_at` TIMESTAMP NULL DEFAULT NULL, `archived_at` TIMESTAMP NULL DEFAULT NULL,
`archived_by` BIGINT UNSIGNED NULL, `archived_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_contracts_number` (`contract_number`), UNIQUE KEY `uq_hr_contracts_number` (`contract_number`),
INDEX `idx_hr_contracts_employee` (`employee_id`), INDEX `idx_hr_contracts_employee` (`employee_profile_id`),
INDEX `idx_hr_contracts_status` (`status`), INDEX `idx_hr_contracts_status` (`status`),
INDEX `idx_hr_contracts_dates` (`start_date`, `end_date`), INDEX `idx_hr_contracts_dates` (`start_date`, `end_date`),
INDEX `idx_hr_contracts_workflow` (`workflow_instance_id`), INDEX `idx_hr_contracts_workflow` (`workflow_instance_id`),
INDEX `idx_hr_contracts_archived` (`is_archived`), INDEX `idx_hr_contracts_archived` (`is_archived`),
CONSTRAINT `fk_hr_contracts_employee` FOREIGN KEY (`employee_id`) REFERENCES `employees`(`id`), CONSTRAINT `fk_hr_contracts_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`),
CONSTRAINT `fk_hr_contracts_renewal` FOREIGN KEY (`renewal_of_contract_id`) REFERENCES `hr_contracts`(`id`) ON DELETE SET NULL, CONSTRAINT `fk_hr_contracts_renewal` FOREIGN KEY (`renewal_of_contract_id`) REFERENCES `hr_contracts`(`id`) ON DELETE SET NULL,
CONSTRAINT `fk_hr_contracts_workflow` FOREIGN KEY (`workflow_instance_id`) REFERENCES `workflow_instances`(`id`) ON DELETE SET NULL, CONSTRAINT `fk_hr_contracts_workflow` FOREIGN KEY (`workflow_instance_id`) REFERENCES `workflow_instances`(`id`) ON DELETE SET NULL,
CONSTRAINT `chk_hr_contracts_type` CHECK (`contract_type` IN ('definite', 'indefinite')), CONSTRAINT `chk_hr_contracts_type` CHECK (`contract_type` IN ('definite', 'indefinite')),
......
...@@ -5,7 +5,7 @@ return [ ...@@ -5,7 +5,7 @@ return [
'up' => " 'up' => "
CREATE TABLE IF NOT EXISTS `hr_employee_documents` ( CREATE TABLE IF NOT EXISTS `hr_employee_documents` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`employee_id` BIGINT UNSIGNED NOT NULL, `employee_profile_id` BIGINT UNSIGNED NOT NULL,
`document_type` VARCHAR(50) NOT NULL COMMENT 'national_id_copy, birth_certificate, degree, military_cert, insurance_form1, insurance_form2, insurance_form6, tax_card, criminal_record, medical_report, contract_copy, photo, experience_cert, other', `document_type` VARCHAR(50) NOT NULL COMMENT 'national_id_copy, birth_certificate, degree, military_cert, insurance_form1, insurance_form2, insurance_form6, tax_card, criminal_record, medical_report, contract_copy, photo, experience_cert, other',
`title_ar` VARCHAR(300) NOT NULL, `title_ar` VARCHAR(300) NOT NULL,
`original_filename` VARCHAR(500) NOT NULL, `original_filename` VARCHAR(500) NOT NULL,
...@@ -26,11 +26,11 @@ return [ ...@@ -26,11 +26,11 @@ return [
`is_archived` TINYINT(1) NOT NULL DEFAULT 0, `is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL DEFAULT NULL, `archived_at` TIMESTAMP NULL DEFAULT NULL,
`archived_by` BIGINT UNSIGNED NULL, `archived_by` BIGINT UNSIGNED NULL,
INDEX `idx_hr_docs_employee` (`employee_id`), INDEX `idx_hr_docs_employee` (`employee_profile_id`),
INDEX `idx_hr_docs_type` (`document_type`), INDEX `idx_hr_docs_type` (`document_type`),
INDEX `idx_hr_docs_expiry` (`expiry_date`), INDEX `idx_hr_docs_expiry` (`expiry_date`),
INDEX `idx_hr_docs_archived` (`is_archived`), INDEX `idx_hr_docs_archived` (`is_archived`),
CONSTRAINT `fk_hr_docs_employee` FOREIGN KEY (`employee_id`) REFERENCES `employees`(`id`) CONSTRAINT `fk_hr_docs_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
", ",
'down' => "DROP TABLE IF EXISTS `hr_employee_documents`", 'down' => "DROP TABLE IF EXISTS `hr_employee_documents`",
......
...@@ -5,21 +5,31 @@ return [ ...@@ -5,21 +5,31 @@ return [
'up' => " 'up' => "
CREATE TABLE IF NOT EXISTS `hr_work_schedules` ( CREATE TABLE IF NOT EXISTS `hr_work_schedules` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`schedule_code` VARCHAR(30) NOT NULL, `code` VARCHAR(30) NOT NULL,
`name_ar` VARCHAR(200) NOT NULL, `name_ar` VARCHAR(200) NOT NULL,
`name_en` VARCHAR(200) NULL, `name_en` VARCHAR(200) NULL,
`schedule_type` VARCHAR(20) NOT NULL DEFAULT 'fixed' COMMENT 'fixed, rotating, flexible', `schedule_type` VARCHAR(20) NOT NULL DEFAULT 'fixed' COMMENT 'fixed, rotating, flexible',
`work_days_json` JSON NOT NULL, `working_days_per_week` INT UNSIGNED NOT NULL DEFAULT 6,
`hours_per_day` DECIMAL(3,1) NOT NULL DEFAULT 8.0, `hours_per_day` DECIMAL(4,2) NOT NULL DEFAULT 8.00,
`ramadan_hours_per_day` DECIMAL(3,1) NOT NULL DEFAULT 7.0, `hours_per_week` DECIMAL(5,2) NOT NULL DEFAULT 48.00,
`night_shift` TINYINT(1) NOT NULL DEFAULT 0, `ramadan_hours_per_day` DECIMAL(4,2) NOT NULL DEFAULT 7.00,
`is_night_shift` TINYINT(1) NOT NULL DEFAULT 0,
`start_time` TIME NULL,
`end_time` TIME NULL,
`break_duration_minutes` INT UNSIGNED NOT NULL DEFAULT 60,
`late_tolerance_minutes` INT UNSIGNED NOT NULL DEFAULT 15,
`early_leave_tolerance` INT UNSIGNED NOT NULL DEFAULT 15,
`days_config_json` JSON NOT NULL,
`is_default` TINYINT(1) NOT NULL DEFAULT 0, `is_default` TINYINT(1) NOT NULL DEFAULT 0,
`is_active` TINYINT(1) NOT NULL DEFAULT 1, `is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL, `created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL, `updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_work_schedules_code` (`schedule_code`), `is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL DEFAULT NULL,
`archived_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_work_schedules_code` (`code`),
INDEX `idx_hr_work_schedules_active` (`is_active`), INDEX `idx_hr_work_schedules_active` (`is_active`),
CONSTRAINT `chk_hr_work_sched_type` CHECK (`schedule_type` IN ('fixed', 'rotating', 'flexible')) CONSTRAINT `chk_hr_work_sched_type` CHECK (`schedule_type` IN ('fixed', 'rotating', 'flexible'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
......
...@@ -5,7 +5,7 @@ return [ ...@@ -5,7 +5,7 @@ return [
'up' => " 'up' => "
CREATE TABLE IF NOT EXISTS `hr_employee_schedules` ( CREATE TABLE IF NOT EXISTS `hr_employee_schedules` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`employee_id` BIGINT UNSIGNED NOT NULL, `employee_profile_id` BIGINT UNSIGNED NOT NULL,
`schedule_id` BIGINT UNSIGNED NOT NULL, `schedule_id` BIGINT UNSIGNED NOT NULL,
`effective_from` DATE NOT NULL, `effective_from` DATE NOT NULL,
`effective_to` DATE NULL, `effective_to` DATE NULL,
...@@ -14,10 +14,10 @@ return [ ...@@ -14,10 +14,10 @@ return [
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL, `created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL, `updated_by` BIGINT UNSIGNED NULL,
INDEX `idx_hr_emp_sched_employee` (`employee_id`), INDEX `idx_hr_emp_sched_employee` (`employee_profile_id`),
INDEX `idx_hr_emp_sched_schedule` (`schedule_id`), INDEX `idx_hr_emp_sched_schedule` (`schedule_id`),
INDEX `idx_hr_emp_sched_dates` (`effective_from`, `effective_to`), INDEX `idx_hr_emp_sched_dates` (`effective_from`, `effective_to`),
CONSTRAINT `fk_hr_emp_sched_employee` FOREIGN KEY (`employee_id`) REFERENCES `employees`(`id`), CONSTRAINT `fk_hr_emp_sched_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`),
CONSTRAINT `fk_hr_emp_sched_schedule` FOREIGN KEY (`schedule_id`) REFERENCES `hr_work_schedules`(`id`) CONSTRAINT `fk_hr_emp_sched_schedule` FOREIGN KEY (`schedule_id`) REFERENCES `hr_work_schedules`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
", ",
......
...@@ -5,7 +5,7 @@ return [ ...@@ -5,7 +5,7 @@ return [
'up' => " 'up' => "
CREATE TABLE IF NOT EXISTS `hr_attendance` ( CREATE TABLE IF NOT EXISTS `hr_attendance` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`employee_id` BIGINT UNSIGNED NOT NULL, `employee_profile_id` BIGINT UNSIGNED NOT NULL,
`attendance_date` DATE NOT NULL, `attendance_date` DATE NOT NULL,
`check_in_time` TIME NULL, `check_in_time` TIME NULL,
`check_out_time` TIME NULL, `check_out_time` TIME NULL,
...@@ -16,21 +16,25 @@ return [ ...@@ -16,21 +16,25 @@ return [
`overtime_type` VARCHAR(10) NOT NULL DEFAULT 'none' COMMENT 'none, day, night', `overtime_type` VARCHAR(10) NOT NULL DEFAULT 'none' COMMENT 'none, day, night',
`late_minutes` INT UNSIGNED NOT NULL DEFAULT 0, `late_minutes` INT UNSIGNED NOT NULL DEFAULT 0,
`early_leave_minutes` INT UNSIGNED NOT NULL DEFAULT 0, `early_leave_minutes` INT UNSIGNED NOT NULL DEFAULT 0,
`status` VARCHAR(20) NOT NULL COMMENT 'present, absent, leave, holiday, weekend, half_day', `status` VARCHAR(20) NOT NULL COMMENT 'present, absent, late, leave, holiday, rest_day, half_day',
`is_ramadan_schedule` TINYINT(1) NOT NULL DEFAULT 0, `is_ramadan_schedule` TINYINT(1) NOT NULL DEFAULT 0,
`notes` TEXT NULL, `notes` TEXT NULL,
`is_approved` TINYINT(1) NOT NULL DEFAULT 0,
`approved_by` BIGINT UNSIGNED NULL, `approved_by` BIGINT UNSIGNED NULL,
`approved_at` TIMESTAMP NULL DEFAULT NULL, `approved_at` TIMESTAMP NULL DEFAULT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL, `created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL, `updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_attendance_emp_date` (`employee_id`, `attendance_date`), `is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL DEFAULT NULL,
`archived_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_attendance_emp_date` (`employee_profile_id`, `attendance_date`),
INDEX `idx_hr_attendance_date` (`attendance_date`), INDEX `idx_hr_attendance_date` (`attendance_date`),
INDEX `idx_hr_attendance_status` (`status`), INDEX `idx_hr_attendance_status` (`status`),
INDEX `idx_hr_attendance_employee` (`employee_id`), INDEX `idx_hr_attendance_employee` (`employee_profile_id`),
CONSTRAINT `fk_hr_attendance_employee` FOREIGN KEY (`employee_id`) REFERENCES `employees`(`id`), CONSTRAINT `fk_hr_attendance_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`),
CONSTRAINT `chk_hr_attendance_status` CHECK (`status` IN ('present', 'absent', 'leave', 'holiday', 'weekend', 'half_day')), CONSTRAINT `chk_hr_attendance_status` CHECK (`status` IN ('present', 'absent', 'late', 'leave', 'holiday', 'rest_day', 'half_day')),
CONSTRAINT `chk_hr_attendance_ot_type` CHECK (`overtime_type` IN ('none', 'day', 'night')) CONSTRAINT `chk_hr_attendance_ot_type` CHECK (`overtime_type` IN ('none', 'day', 'night'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
", ",
......
...@@ -5,34 +5,46 @@ return [ ...@@ -5,34 +5,46 @@ return [
'up' => " 'up' => "
CREATE TABLE IF NOT EXISTS `hr_leave_types` ( CREATE TABLE IF NOT EXISTS `hr_leave_types` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`leave_code` VARCHAR(30) NOT NULL, `code` VARCHAR(30) NOT NULL,
`name_ar` VARCHAR(200) NOT NULL, `name_ar` VARCHAR(200) NOT NULL,
`name_en` VARCHAR(200) NULL, `name_en` VARCHAR(200) NULL,
`category` VARCHAR(30) NULL COMMENT 'annual, sick, special, casual, unpaid',
`default_days_per_year` DECIMAL(5,1) NULL, `default_days_per_year` DECIMAL(5,1) NULL,
`max_days_per_year` DECIMAL(5,1) NULL,
`is_paid` TINYINT(1) NOT NULL DEFAULT 1, `is_paid` TINYINT(1) NOT NULL DEFAULT 1,
`pay_percentage` DECIMAL(5,2) NOT NULL DEFAULT 100.00, `pay_percentage` DECIMAL(5,2) NOT NULL DEFAULT 100.00,
`requires_approval` TINYINT(1) NOT NULL DEFAULT 1, `requires_approval` TINYINT(1) NOT NULL DEFAULT 1,
`requires_document` TINYINT(1) NOT NULL DEFAULT 0, `requires_attachment` TINYINT(1) NOT NULL DEFAULT 0,
`min_days_per_request` DECIMAL(5,1) NULL,
`max_days_per_request` DECIMAL(5,1) NULL,
`min_consecutive_annual` DECIMAL(5,1) NULL,
`max_consecutive_days` INT UNSIGNED NULL, `max_consecutive_days` INT UNSIGNED NULL,
`max_per_occurrence` INT UNSIGNED NULL, `max_per_occurrence` INT UNSIGNED NULL,
`max_times_in_career` INT UNSIGNED NULL, `max_times_in_career` INT UNSIGNED NULL,
`min_service_months` INT UNSIGNED NULL, `min_service_months` INT UNSIGNED NULL,
`gender_restriction` VARCHAR(10) NOT NULL DEFAULT 'all' COMMENT 'male, female, all', `advance_notice_days` INT UNSIGNED NOT NULL DEFAULT 0,
`gender_restriction` VARCHAR(10) NULL DEFAULT NULL COMMENT 'male, female, null=all',
`carry_over_allowed` TINYINT(1) NOT NULL DEFAULT 0, `carry_over_allowed` TINYINT(1) NOT NULL DEFAULT 0,
`carry_over_max_days` INT UNSIGNED NULL, `max_carry_over_days` DECIMAL(5,1) NULL,
`carry_over_expiry_months` INT UNSIGNED NULL, `carry_over_expiry_months` INT UNSIGNED NULL,
`is_accumulative` TINYINT(1) NOT NULL DEFAULT 0, `is_accumulative` TINYINT(1) NOT NULL DEFAULT 0,
`max_accumulation_years` INT UNSIGNED NULL DEFAULT 3, `accumulation_max_years` INT UNSIGNED NULL DEFAULT 3,
`career_max_times` INT UNSIGNED NULL,
`career_max_days` DECIMAL(5,1) NULL,
`deduct_from_salary` TINYINT(1) NOT NULL DEFAULT 0, `deduct_from_salary` TINYINT(1) NOT NULL DEFAULT 0,
`legal_reference` VARCHAR(100) NULL,
`rules_json` JSON NULL,
`is_active` TINYINT(1) NOT NULL DEFAULT 1, `is_active` TINYINT(1) NOT NULL DEFAULT 1,
`sort_order` INT UNSIGNED NOT NULL DEFAULT 0, `sort_order` INT UNSIGNED NOT NULL DEFAULT 0,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL, `created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL, `updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_leave_types_code` (`leave_code`), `is_archived` TINYINT(1) NOT NULL DEFAULT 0,
INDEX `idx_hr_leave_types_active` (`is_active`), `archived_at` TIMESTAMP NULL DEFAULT NULL,
CONSTRAINT `chk_hr_leave_gender` CHECK (`gender_restriction` IN ('male', 'female', 'all')) `archived_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_leave_types_code` (`code`),
INDEX `idx_hr_leave_types_active` (`is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
", ",
'down' => "DROP TABLE IF EXISTS `hr_leave_types`", 'down' => "DROP TABLE IF EXISTS `hr_leave_types`",
......
...@@ -5,7 +5,7 @@ return [ ...@@ -5,7 +5,7 @@ return [
'up' => " 'up' => "
CREATE TABLE IF NOT EXISTS `hr_leave_balances` ( CREATE TABLE IF NOT EXISTS `hr_leave_balances` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`employee_id` BIGINT UNSIGNED NOT NULL, `employee_profile_id` BIGINT UNSIGNED NOT NULL,
`leave_type_id` BIGINT UNSIGNED NOT NULL, `leave_type_id` BIGINT UNSIGNED NOT NULL,
`year` YEAR NOT NULL, `year` YEAR NOT NULL,
`entitled_days` DECIMAL(5,1) NOT NULL DEFAULT 0.0, `entitled_days` DECIMAL(5,1) NOT NULL DEFAULT 0.0,
...@@ -18,11 +18,11 @@ return [ ...@@ -18,11 +18,11 @@ return [
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL, `created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL, `updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_leave_bal` (`employee_id`, `leave_type_id`, `year`), UNIQUE KEY `uq_hr_leave_bal` (`employee_profile_id`, `leave_type_id`, `year`),
INDEX `idx_hr_leave_bal_employee` (`employee_id`), INDEX `idx_hr_leave_bal_employee` (`employee_profile_id`),
INDEX `idx_hr_leave_bal_year` (`year`), INDEX `idx_hr_leave_bal_year` (`year`),
INDEX `idx_hr_leave_bal_type` (`leave_type_id`), INDEX `idx_hr_leave_bal_type` (`leave_type_id`),
CONSTRAINT `fk_hr_leave_bal_employee` FOREIGN KEY (`employee_id`) REFERENCES `employees`(`id`), CONSTRAINT `fk_hr_leave_bal_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`),
CONSTRAINT `fk_hr_leave_bal_type` FOREIGN KEY (`leave_type_id`) REFERENCES `hr_leave_types`(`id`) CONSTRAINT `fk_hr_leave_bal_type` FOREIGN KEY (`leave_type_id`) REFERENCES `hr_leave_types`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
", ",
......
...@@ -5,7 +5,7 @@ return [ ...@@ -5,7 +5,7 @@ return [
'up' => " 'up' => "
CREATE TABLE IF NOT EXISTS `hr_leave_requests` ( CREATE TABLE IF NOT EXISTS `hr_leave_requests` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`employee_id` BIGINT UNSIGNED NOT NULL, `employee_profile_id` BIGINT UNSIGNED NOT NULL,
`leave_type_id` BIGINT UNSIGNED NOT NULL, `leave_type_id` BIGINT UNSIGNED NOT NULL,
`start_date` DATE NOT NULL, `start_date` DATE NOT NULL,
`end_date` DATE NOT NULL, `end_date` DATE NOT NULL,
...@@ -28,13 +28,13 @@ return [ ...@@ -28,13 +28,13 @@ return [
`is_archived` TINYINT(1) NOT NULL DEFAULT 0, `is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL DEFAULT NULL, `archived_at` TIMESTAMP NULL DEFAULT NULL,
`archived_by` BIGINT UNSIGNED NULL, `archived_by` BIGINT UNSIGNED NULL,
INDEX `idx_hr_leave_req_employee` (`employee_id`), INDEX `idx_hr_leave_req_employee` (`employee_profile_id`),
INDEX `idx_hr_leave_req_type` (`leave_type_id`), INDEX `idx_hr_leave_req_type` (`leave_type_id`),
INDEX `idx_hr_leave_req_dates` (`start_date`, `end_date`), INDEX `idx_hr_leave_req_dates` (`start_date`, `end_date`),
INDEX `idx_hr_leave_req_status` (`status`), INDEX `idx_hr_leave_req_status` (`status`),
INDEX `idx_hr_leave_req_workflow` (`workflow_instance_id`), INDEX `idx_hr_leave_req_workflow` (`workflow_instance_id`),
INDEX `idx_hr_leave_req_archived` (`is_archived`), INDEX `idx_hr_leave_req_archived` (`is_archived`),
CONSTRAINT `fk_hr_leave_req_employee` FOREIGN KEY (`employee_id`) REFERENCES `employees`(`id`), CONSTRAINT `fk_hr_leave_req_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`),
CONSTRAINT `fk_hr_leave_req_type` FOREIGN KEY (`leave_type_id`) REFERENCES `hr_leave_types`(`id`), CONSTRAINT `fk_hr_leave_req_type` FOREIGN KEY (`leave_type_id`) REFERENCES `hr_leave_types`(`id`),
CONSTRAINT `fk_hr_leave_req_approver` FOREIGN KEY (`approved_by`) REFERENCES `employees`(`id`) ON DELETE SET NULL, CONSTRAINT `fk_hr_leave_req_approver` FOREIGN KEY (`approved_by`) REFERENCES `employees`(`id`) ON DELETE SET NULL,
CONSTRAINT `fk_hr_leave_req_workflow` FOREIGN KEY (`workflow_instance_id`) REFERENCES `workflow_instances`(`id`) ON DELETE SET NULL, CONSTRAINT `fk_hr_leave_req_workflow` FOREIGN KEY (`workflow_instance_id`) REFERENCES `workflow_instances`(`id`) ON DELETE SET NULL,
......
...@@ -5,24 +5,28 @@ return [ ...@@ -5,24 +5,28 @@ return [
'up' => " 'up' => "
CREATE TABLE IF NOT EXISTS `hr_holidays` ( CREATE TABLE IF NOT EXISTS `hr_holidays` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`holiday_date` DATE NULL,
`name_ar` VARCHAR(200) NOT NULL, `name_ar` VARCHAR(200) NOT NULL,
`name_en` VARCHAR(200) NULL, `name_en` VARCHAR(200) NULL,
`holiday_type` VARCHAR(20) NOT NULL COMMENT 'national, religious, company', `holiday_type` VARCHAR(20) NOT NULL COMMENT 'national, religious, company',
`religion_specific` VARCHAR(20) NOT NULL DEFAULT 'all' COMMENT 'all, muslim, christian', `date` DATE NULL,
`duration_days` INT UNSIGNED NOT NULL DEFAULT 1,
`is_recurring` TINYINT(1) NOT NULL DEFAULT 0, `is_recurring` TINYINT(1) NOT NULL DEFAULT 0,
`recurring_month` INT UNSIGNED NULL, `recurring_month` INT UNSIGNED NULL,
`recurring_day` INT UNSIGNED NULL, `recurring_day` INT UNSIGNED NULL,
`religion` VARCHAR(20) NULL DEFAULT NULL COMMENT 'null=all, muslim, christian',
`year` YEAR NULL, `year` YEAR NULL,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL, `created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL, `updated_by` BIGINT UNSIGNED NULL,
INDEX `idx_hr_holidays_date` (`holiday_date`), `is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL DEFAULT NULL,
`archived_by` BIGINT UNSIGNED NULL,
INDEX `idx_hr_holidays_date` (`date`),
INDEX `idx_hr_holidays_year` (`year`), INDEX `idx_hr_holidays_year` (`year`),
INDEX `idx_hr_holidays_type` (`holiday_type`), INDEX `idx_hr_holidays_type` (`holiday_type`),
CONSTRAINT `chk_hr_holidays_type` CHECK (`holiday_type` IN ('national', 'religious', 'company')), CONSTRAINT `chk_hr_holidays_type` CHECK (`holiday_type` IN ('national', 'religious', 'company'))
CONSTRAINT `chk_hr_holidays_religion` CHECK (`religion_specific` IN ('all', 'muslim', 'christian'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
", ",
'down' => "DROP TABLE IF EXISTS `hr_holidays`", 'down' => "DROP TABLE IF EXISTS `hr_holidays`",
......
...@@ -5,7 +5,7 @@ return [ ...@@ -5,7 +5,7 @@ return [
'up' => " 'up' => "
CREATE TABLE IF NOT EXISTS `hr_disciplinary_actions` ( CREATE TABLE IF NOT EXISTS `hr_disciplinary_actions` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`employee_id` BIGINT UNSIGNED NOT NULL, `employee_profile_id` BIGINT UNSIGNED NOT NULL,
`action_number` VARCHAR(30) NOT NULL, `action_number` VARCHAR(30) NOT NULL,
`incident_date` DATE NOT NULL, `incident_date` DATE NOT NULL,
`incident_description` TEXT NOT NULL, `incident_description` TEXT NOT NULL,
...@@ -38,12 +38,12 @@ return [ ...@@ -38,12 +38,12 @@ return [
`archived_at` TIMESTAMP NULL DEFAULT NULL, `archived_at` TIMESTAMP NULL DEFAULT NULL,
`archived_by` BIGINT UNSIGNED NULL, `archived_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_disciplinary_number` (`action_number`), UNIQUE KEY `uq_hr_disciplinary_number` (`action_number`),
INDEX `idx_hr_disc_employee` (`employee_id`), INDEX `idx_hr_disc_employee` (`employee_profile_id`),
INDEX `idx_hr_disc_status` (`status`), INDEX `idx_hr_disc_status` (`status`),
INDEX `idx_hr_disc_type` (`penalty_type`), INDEX `idx_hr_disc_type` (`penalty_type`),
INDEX `idx_hr_disc_date` (`incident_date`), INDEX `idx_hr_disc_date` (`incident_date`),
INDEX `idx_hr_disc_archived` (`is_archived`), INDEX `idx_hr_disc_archived` (`is_archived`),
CONSTRAINT `fk_hr_disc_employee` FOREIGN KEY (`employee_id`) REFERENCES `employees`(`id`), CONSTRAINT `fk_hr_disc_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`),
CONSTRAINT `chk_hr_disc_penalty` CHECK (`penalty_type` IN ('warning', 'wage_deduction', 'raise_postponement', 'suspension', 'demotion', 'termination')), CONSTRAINT `chk_hr_disc_penalty` CHECK (`penalty_type` IN ('warning', 'wage_deduction', 'raise_postponement', 'suspension', 'demotion', 'termination')),
CONSTRAINT `chk_hr_disc_status` CHECK (`status` IN ('investigation', 'pending_decision', 'decided', 'appealed', 'appeal_reviewed', 'enforced', 'cancelled')), CONSTRAINT `chk_hr_disc_status` CHECK (`status` IN ('investigation', 'pending_decision', 'decided', 'appealed', 'appeal_reviewed', 'enforced', 'cancelled')),
CONSTRAINT `chk_hr_disc_deduction_days` CHECK (`wage_deduction_days` IS NULL OR `wage_deduction_days` <= 5), CONSTRAINT `chk_hr_disc_deduction_days` CHECK (`wage_deduction_days` IS NULL OR `wage_deduction_days` <= 5),
......
...@@ -5,20 +5,22 @@ return [ ...@@ -5,20 +5,22 @@ return [
'up' => " 'up' => "
CREATE TABLE IF NOT EXISTS `hr_employee_loans` ( CREATE TABLE IF NOT EXISTS `hr_employee_loans` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`employee_id` BIGINT UNSIGNED NOT NULL, `employee_profile_id` BIGINT UNSIGNED NOT NULL,
`loan_number` VARCHAR(30) NOT NULL, `loan_number` VARCHAR(30) NOT NULL,
`loan_type` VARCHAR(30) NOT NULL COMMENT 'salary_advance, personal_loan, emergency_loan', `loan_type` VARCHAR(30) NOT NULL COMMENT 'salary_advance, personal_loan, emergency_loan',
`amount` DECIMAL(15,2) NOT NULL, `loan_amount` DECIMAL(15,2) NOT NULL,
`installment_amount` DECIMAL(15,2) NOT NULL, `installment_amount` DECIMAL(15,2) NOT NULL,
`total_installments` INT UNSIGNED NOT NULL, `number_of_installments` INT UNSIGNED NOT NULL,
`paid_installments` INT UNSIGNED NOT NULL DEFAULT 0, `paid_installments` INT UNSIGNED NOT NULL DEFAULT 0,
`remaining_amount` DECIMAL(15,2) NOT NULL, `remaining_amount` DECIMAL(15,2) NOT NULL,
`interest_rate` DECIMAL(5,2) NOT NULL DEFAULT 0.00, `interest_rate` DECIMAL(5,2) NOT NULL DEFAULT 0.00,
`start_deduction_month` DATE NOT NULL, `request_date` DATE NULL,
`status` VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending, approved, active, completed, cancelled', `start_deduction_date` DATE NOT NULL,
`reason` TEXT NULL,
`status` VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending, approved, disbursed, repaying, settled, rejected, cancelled',
`approved_by` BIGINT UNSIGNED NULL, `approved_by` BIGINT UNSIGNED NULL,
`approved_at` TIMESTAMP NULL DEFAULT NULL, `approved_at` TIMESTAMP NULL DEFAULT NULL,
`disbursement_date` DATE NULL, `disbursed_date` DATE NULL,
`workflow_instance_id` BIGINT UNSIGNED NULL, `workflow_instance_id` BIGINT UNSIGNED NULL,
`notes` TEXT NULL, `notes` TEXT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
...@@ -29,13 +31,13 @@ return [ ...@@ -29,13 +31,13 @@ return [
`archived_at` TIMESTAMP NULL DEFAULT NULL, `archived_at` TIMESTAMP NULL DEFAULT NULL,
`archived_by` BIGINT UNSIGNED NULL, `archived_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_loans_number` (`loan_number`), UNIQUE KEY `uq_hr_loans_number` (`loan_number`),
INDEX `idx_hr_loans_employee` (`employee_id`), INDEX `idx_hr_loans_employee` (`employee_profile_id`),
INDEX `idx_hr_loans_status` (`status`), INDEX `idx_hr_loans_status` (`status`),
INDEX `idx_hr_loans_archived` (`is_archived`), INDEX `idx_hr_loans_archived` (`is_archived`),
CONSTRAINT `fk_hr_loans_employee` FOREIGN KEY (`employee_id`) REFERENCES `employees`(`id`), CONSTRAINT `fk_hr_loans_employee` FOREIGN KEY (`employee_profile_id`) REFERENCES `hr_employee_profiles`(`id`),
CONSTRAINT `chk_hr_loans_type` CHECK (`loan_type` IN ('salary_advance', 'personal_loan', 'emergency_loan')), CONSTRAINT `chk_hr_loans_type` CHECK (`loan_type` IN ('salary_advance', 'personal_loan', 'emergency_loan')),
CONSTRAINT `chk_hr_loans_status` CHECK (`status` IN ('pending', 'approved', 'active', 'completed', 'cancelled')), CONSTRAINT `chk_hr_loans_status` CHECK (`status` IN ('pending', 'approved', 'disbursed', 'repaying', 'settled', 'rejected', 'cancelled')),
CONSTRAINT `chk_hr_loans_amount` CHECK (`amount` > 0) CONSTRAINT `chk_hr_loans_amount` CHECK (`loan_amount` > 0)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
", ",
'down' => "DROP TABLE IF EXISTS `hr_employee_loans`", 'down' => "DROP TABLE IF EXISTS `hr_employee_loans`",
......
...@@ -7,7 +7,7 @@ return [ ...@@ -7,7 +7,7 @@ return [
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`loan_id` BIGINT UNSIGNED NOT NULL, `loan_id` BIGINT UNSIGNED NOT NULL,
`installment_number` INT UNSIGNED NOT NULL, `installment_number` INT UNSIGNED NOT NULL,
`due_month` DATE NOT NULL, `due_date` DATE NOT NULL,
`amount` DECIMAL(15,2) NOT NULL, `amount` DECIMAL(15,2) NOT NULL,
`status` VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending, deducted, skipped, cancelled', `status` VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending, deducted, skipped, cancelled',
`payroll_run_id` BIGINT UNSIGNED NULL, `payroll_run_id` BIGINT UNSIGNED NULL,
...@@ -16,7 +16,7 @@ return [ ...@@ -16,7 +16,7 @@ return [
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_hr_loan_inst_loan` (`loan_id`), INDEX `idx_hr_loan_inst_loan` (`loan_id`),
INDEX `idx_hr_loan_inst_month` (`due_month`), INDEX `idx_hr_loan_inst_month` (`due_date`),
INDEX `idx_hr_loan_inst_status` (`status`), INDEX `idx_hr_loan_inst_status` (`status`),
CONSTRAINT `fk_hr_loan_inst_loan` FOREIGN KEY (`loan_id`) REFERENCES `hr_employee_loans`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_hr_loan_inst_loan` FOREIGN KEY (`loan_id`) REFERENCES `hr_employee_loans`(`id`) ON DELETE CASCADE,
CONSTRAINT `chk_hr_loan_inst_status` CHECK (`status` IN ('pending', 'deducted', 'skipped', 'cancelled')), CONSTRAINT `chk_hr_loan_inst_status` CHECK (`status` IN ('pending', 'deducted', 'skipped', 'cancelled')),
......
...@@ -6,42 +6,47 @@ return [ ...@@ -6,42 +6,47 @@ return [
CREATE TABLE IF NOT EXISTS `hr_payroll_runs` ( CREATE TABLE IF NOT EXISTS `hr_payroll_runs` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`period_id` BIGINT UNSIGNED NOT NULL, `period_id` BIGINT UNSIGNED NOT NULL,
`employee_id` BIGINT UNSIGNED NOT NULL, `employee_profile_id` BIGINT UNSIGNED NOT NULL,
`employee_number` VARCHAR(20) NOT NULL, `employee_number` VARCHAR(20) NOT NULL,
`department_id` BIGINT UNSIGNED NULL, `department_id` BIGINT UNSIGNED NULL,
`job_title_id` BIGINT UNSIGNED NULL, `job_title_id` BIGINT UNSIGNED NULL,
`basic_salary` DECIMAL(15,2) NOT NULL, `basic_salary` DECIMAL(15,2) NOT NULL,
`working_days` INT UNSIGNED NOT NULL DEFAULT 0,
`actual_working_days` INT UNSIGNED NOT NULL DEFAULT 0,
`absent_days` INT UNSIGNED NOT NULL DEFAULT 0,
`leave_days` DECIMAL(5,1) NOT NULL DEFAULT 0.0,
`overtime_hours` DECIMAL(6,2) NOT NULL DEFAULT 0.00,
`overtime_amount` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`gross_earnings` DECIMAL(15,2) NOT NULL DEFAULT 0.00, `gross_earnings` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`total_allowances` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`overtime_amount` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`total_deductions` DECIMAL(15,2) NOT NULL DEFAULT 0.00, `total_deductions` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`net_salary` DECIMAL(15,2) NOT NULL DEFAULT 0.00, `insurance_employee` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`employee_insurance` DECIMAL(15,2) NOT NULL DEFAULT 0.00, `insurance_employer` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`employer_insurance` DECIMAL(15,2) NOT NULL DEFAULT 0.00, `tax_amount` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`income_tax` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`loan_deduction` DECIMAL(15,2) NOT NULL DEFAULT 0.00, `loan_deduction` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`penalty_deduction` DECIMAL(15,2) NOT NULL DEFAULT 0.00, `penalty_deduction` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`absence_deduction` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`other_deductions` DECIMAL(15,2) NOT NULL DEFAULT 0.00, `other_deductions` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`other_earnings` DECIMAL(15,2) NOT NULL DEFAULT 0.00, `other_earnings` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`calculation_json` JSON NOT NULL, `net_salary` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`working_days` INT UNSIGNED NOT NULL DEFAULT 0,
`present_days` INT UNSIGNED NOT NULL DEFAULT 0,
`absent_days` INT UNSIGNED NOT NULL DEFAULT 0,
`leave_days` DECIMAL(5,1) NOT NULL DEFAULT 0.0,
`overtime_hours` DECIMAL(6,2) NOT NULL DEFAULT 0.00,
`calculation_json` JSON NULL,
`status` VARCHAR(20) NOT NULL DEFAULT 'calculated' COMMENT 'calculated, approved, paid, cancelled', `status` VARCHAR(20) NOT NULL DEFAULT 'calculated' COMMENT 'calculated, approved, paid, cancelled',
`payment_method` VARCHAR(20) NULL COMMENT 'bank_transfer, cash, check', `payment_method` VARCHAR(20) NULL COMMENT 'bank_transfer, cash, check',
`payment_reference` VARCHAR(100) NULL,
`paid_at` TIMESTAMP NULL DEFAULT NULL, `paid_at` TIMESTAMP NULL DEFAULT NULL,
`notes` TEXT NULL, `notes` TEXT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL, `created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL, `updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_payroll_run` (`period_id`, `employee_id`), `is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL DEFAULT NULL,
`archived_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_payroll_run` (`period_id`, `employee_profile_id`),
INDEX `idx_hr_payroll_run_period` (`period_id`), INDEX `idx_hr_payroll_run_period` (`period_id`),
INDEX `idx_hr_payroll_run_employee` (`employee_id`), INDEX `idx_hr_payroll_run_employee` (`employee_profile_id`),
INDEX `idx_hr_payroll_run_status` (`status`), INDEX `idx_hr_payroll_run_status` (`status`),
CONSTRAINT `fk_hr_payroll_run_period` FOREIGN KEY (`period_id`) REFERENCES `hr_payroll_periods`(`id`), CONSTRAINT `fk_hr_payroll_run_period` FOREIGN KEY (`period_id`) REFERENCES `hr_payroll_periods`(`id`),
CONSTRAINT `fk_hr_payroll_run_employee` FOREIGN KEY (`employee_id`) REFERENCES `employees`(`id`),
CONSTRAINT `chk_hr_payroll_run_status` CHECK (`status` IN ('calculated', 'approved', 'paid', 'cancelled')), CONSTRAINT `chk_hr_payroll_run_status` CHECK (`status` IN ('calculated', 'approved', 'paid', 'cancelled')),
CONSTRAINT `chk_hr_payroll_run_gross` CHECK (`gross_earnings` >= 0), CONSTRAINT `chk_hr_payroll_run_gross` CHECK (`gross_earnings` >= 0),
CONSTRAINT `chk_hr_payroll_run_net` CHECK (`net_salary` >= 0) CONSTRAINT `chk_hr_payroll_run_net` CHECK (`net_salary` >= 0)
......
...@@ -8,8 +8,8 @@ return [ ...@@ -8,8 +8,8 @@ return [
`payroll_run_id` BIGINT UNSIGNED NOT NULL, `payroll_run_id` BIGINT UNSIGNED NOT NULL,
`component_id` BIGINT UNSIGNED NULL, `component_id` BIGINT UNSIGNED NULL,
`component_code` VARCHAR(30) NOT NULL, `component_code` VARCHAR(30) NOT NULL,
`component_name_ar` VARCHAR(200) NOT NULL, `name_ar` VARCHAR(200) NOT NULL,
`component_type` VARCHAR(20) NOT NULL COMMENT 'earning, deduction', `type` VARCHAR(20) NOT NULL COMMENT 'earning, deduction',
`amount` DECIMAL(15,2) NOT NULL, `amount` DECIMAL(15,2) NOT NULL,
`is_taxable` TINYINT(1) NOT NULL DEFAULT 1, `is_taxable` TINYINT(1) NOT NULL DEFAULT 1,
`is_insurable` TINYINT(1) NOT NULL DEFAULT 1, `is_insurable` TINYINT(1) NOT NULL DEFAULT 1,
...@@ -18,7 +18,7 @@ return [ ...@@ -18,7 +18,7 @@ return [
INDEX `idx_hr_payroll_comp_run` (`payroll_run_id`), INDEX `idx_hr_payroll_comp_run` (`payroll_run_id`),
INDEX `idx_hr_payroll_comp_code` (`component_code`), INDEX `idx_hr_payroll_comp_code` (`component_code`),
CONSTRAINT `fk_hr_payroll_comp_run` FOREIGN KEY (`payroll_run_id`) REFERENCES `hr_payroll_runs`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_hr_payroll_comp_run` FOREIGN KEY (`payroll_run_id`) REFERENCES `hr_payroll_runs`(`id`) ON DELETE CASCADE,
CONSTRAINT `chk_hr_payroll_comp_type` CHECK (`component_type` IN ('earning', 'deduction')) CONSTRAINT `chk_hr_payroll_comp_type` CHECK (`type` IN ('earning', 'deduction'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
", ",
'down' => "DROP TABLE IF EXISTS `hr_payroll_components_log`", 'down' => "DROP TABLE IF EXISTS `hr_payroll_components_log`",
......
...@@ -5,29 +5,31 @@ return [ ...@@ -5,29 +5,31 @@ return [
'up' => " 'up' => "
CREATE TABLE IF NOT EXISTS `hr_insurance_records` ( CREATE TABLE IF NOT EXISTS `hr_insurance_records` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`employee_id` BIGINT UNSIGNED NOT NULL, `employee_profile_id` BIGINT UNSIGNED NOT NULL,
`period_id` BIGINT UNSIGNED NOT NULL, `period_id` BIGINT UNSIGNED NOT NULL,
`payroll_run_id` BIGINT UNSIGNED NULL, `payroll_run_id` BIGINT UNSIGNED NULL,
`insurable_salary` DECIMAL(15,2) NOT NULL, `basic_insurable_salary` DECIMAL(15,2) NOT NULL,
`basic_salary_portion` DECIMAL(15,2) NOT NULL, `variable_insurable_salary` DECIMAL(15,2) NOT NULL,
`variable_salary_portion` DECIMAL(15,2) NOT NULL, `employee_basic_share` DECIMAL(15,2) NOT NULL,
`employee_share_basic` DECIMAL(15,2) NOT NULL, `employee_variable_share` DECIMAL(15,2) NOT NULL,
`employee_share_variable` DECIMAL(15,2) NOT NULL,
`employer_share_basic` DECIMAL(15,2) NOT NULL,
`employer_share_variable` DECIMAL(15,2) NOT NULL,
`total_employee_share` DECIMAL(15,2) NOT NULL, `total_employee_share` DECIMAL(15,2) NOT NULL,
`employer_basic_share` DECIMAL(15,2) NOT NULL,
`employer_variable_share` DECIMAL(15,2) NOT NULL,
`total_employer_share` DECIMAL(15,2) NOT NULL, `total_employer_share` DECIMAL(15,2) NOT NULL,
`total_contribution` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`insurance_number` VARCHAR(20) NULL, `insurance_number` VARCHAR(20) NULL,
`notes` TEXT NULL, `notes` TEXT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL, `created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL, `updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_insurance_emp_period` (`employee_id`, `period_id`), `is_archived` TINYINT(1) NOT NULL DEFAULT 0,
INDEX `idx_hr_insurance_employee` (`employee_id`), `archived_at` TIMESTAMP NULL DEFAULT NULL,
`archived_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_insurance_emp_period` (`employee_profile_id`, `period_id`),
INDEX `idx_hr_insurance_employee` (`employee_profile_id`),
INDEX `idx_hr_insurance_period` (`period_id`), INDEX `idx_hr_insurance_period` (`period_id`),
INDEX `idx_hr_insurance_payroll` (`payroll_run_id`), INDEX `idx_hr_insurance_payroll` (`payroll_run_id`),
CONSTRAINT `fk_hr_insurance_employee` FOREIGN KEY (`employee_id`) REFERENCES `employees`(`id`),
CONSTRAINT `fk_hr_insurance_period` FOREIGN KEY (`period_id`) REFERENCES `hr_payroll_periods`(`id`), CONSTRAINT `fk_hr_insurance_period` FOREIGN KEY (`period_id`) REFERENCES `hr_payroll_periods`(`id`),
CONSTRAINT `fk_hr_insurance_payroll` FOREIGN KEY (`payroll_run_id`) REFERENCES `hr_payroll_runs`(`id`) ON DELETE SET NULL CONSTRAINT `fk_hr_insurance_payroll` FOREIGN KEY (`payroll_run_id`) REFERENCES `hr_payroll_runs`(`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
......
...@@ -8,21 +8,25 @@ return [ ...@@ -8,21 +8,25 @@ return [
`employee_id` BIGINT UNSIGNED NOT NULL, `employee_id` BIGINT UNSIGNED NOT NULL,
`period_id` BIGINT UNSIGNED NOT NULL, `period_id` BIGINT UNSIGNED NOT NULL,
`payroll_run_id` BIGINT UNSIGNED NULL, `payroll_run_id` BIGINT UNSIGNED NULL,
`month` INT UNSIGNED NULL,
`gross_taxable_monthly` DECIMAL(15,2) NOT NULL, `gross_taxable_monthly` DECIMAL(15,2) NOT NULL,
`annual_taxable_income` DECIMAL(15,2) NOT NULL, `annual_taxable_income` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`personal_exemption` DECIMAL(15,2) NOT NULL, `personal_exemption` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`insurance_exemption` DECIMAL(15,2) NOT NULL, `insurance_exemption` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`net_taxable_income` DECIMAL(15,2) NOT NULL, `net_taxable_income` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`annual_tax` DECIMAL(15,2) NOT NULL, `annual_tax` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`monthly_tax` DECIMAL(15,2) NOT NULL, `monthly_tax` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`ytd_tax` DECIMAL(15,2) NOT NULL, `ytd_tax` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`ytd_taxable_income` DECIMAL(15,2) NOT NULL, `ytd_taxable_income` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
`calculation_json` JSON NOT NULL, `calculation_json` JSON NULL,
`notes` TEXT NULL, `notes` TEXT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL, `created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL, `updated_by` BIGINT UNSIGNED NULL,
`is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL DEFAULT NULL,
`archived_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_hr_tax_emp_period` (`employee_id`, `period_id`), UNIQUE KEY `uq_hr_tax_emp_period` (`employee_id`, `period_id`),
INDEX `idx_hr_tax_employee` (`employee_id`), INDEX `idx_hr_tax_employee` (`employee_id`),
INDEX `idx_hr_tax_period` (`period_id`), INDEX `idx_hr_tax_period` (`period_id`),
......
...@@ -233,11 +233,14 @@ return function (Database $db): void { ...@@ -233,11 +233,14 @@ return function (Database $db): void {
continue; continue;
} }
$db->insert('system_config', [ $db->insert('system_config', [
'config_key' => $cfg['config_key'], 'config_key' => $cfg['config_key'],
'config_value' => $cfg['config_value'], 'config_value' => $cfg['config_value'],
'description' => $cfg['description'], 'config_type' => is_numeric($cfg['config_value']) ? (str_contains($cfg['config_value'], '.') ? 'float' : 'integer') : (str_starts_with($cfg['config_value'], '[') ? 'json' : 'string'),
'created_at' => $ts, 'group_name' => 'hr',
'updated_at' => $ts, 'description_ar' => $cfg['description'],
'is_editable' => 1,
'created_at' => $ts,
'updated_at' => $ts,
]); ]);
} }
}; };
...@@ -83,15 +83,14 @@ return function (Database $db): void { ...@@ -83,15 +83,14 @@ return function (Database $db): void {
} }
$db->insert('sms_templates', [ $db->insert('sms_templates', [
'template_code' => $tpl['template_code'], 'template_code' => $tpl['template_code'],
'name_ar' => $tpl['name_ar'], 'name_ar' => $tpl['name_ar'],
'name_en' => $tpl['name_en'], 'name_en' => $tpl['name_en'],
'body_ar' => $tpl['body_ar'], 'message_template_ar' => $tpl['body_ar'],
'channel' => $tpl['channel'], 'trigger_event' => $tpl['event_trigger'],
'event_trigger' => $tpl['event_trigger'], 'is_active' => 1,
'is_active' => 1, 'created_at' => $ts,
'created_at' => $ts, 'updated_at' => $ts,
'updated_at' => $ts,
]); ]);
} }
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment