Commit 3fcec655 authored by Mahmoud Aglan's avatar Mahmoud Aglan

fixhe

parent f99774c0
...@@ -54,6 +54,7 @@ class ContractController extends Controller ...@@ -54,6 +54,7 @@ class ContractController extends Controller
'working_hours_per_day' => trim((string) $request->post('working_hours_per_day', '8')), 'working_hours_per_day' => trim((string) $request->post('working_hours_per_day', '8')),
'notice_period_months' => (int) $request->post('notice_period_months', 2), 'notice_period_months' => (int) $request->post('notice_period_months', 2),
'basic_salary' => trim((string) $request->post('basic_salary', '0.00')), 'basic_salary' => trim((string) $request->post('basic_salary', '0.00')),
'total_package' => trim((string) $request->post('total_package', '')) ?: null,
'terms_ar' => trim((string) $request->post('terms_ar', '')) ?: null, 'terms_ar' => trim((string) $request->post('terms_ar', '')) ?: null,
'notes' => trim((string) $request->post('notes', '')) ?: null, 'notes' => trim((string) $request->post('notes', '')) ?: null,
]; ];
...@@ -133,6 +134,7 @@ class ContractController extends Controller ...@@ -133,6 +134,7 @@ class ContractController extends Controller
return $this->redirect('/hr/contracts/' . $id)->withError('لا يمكن تعديل عقد غير مسودة'); return $this->redirect('/hr/contracts/' . $id)->withError('لا يمكن تعديل عقد غير مسودة');
} }
$termsAr = trim((string) $request->post('terms_ar', '')) ?: null;
$data = [ $data = [
'contract_type' => trim((string) $request->post('contract_type', 'definite')), 'contract_type' => trim((string) $request->post('contract_type', 'definite')),
'start_date' => trim((string) $request->post('start_date', '')), 'start_date' => trim((string) $request->post('start_date', '')),
...@@ -141,7 +143,8 @@ class ContractController extends Controller ...@@ -141,7 +143,8 @@ class ContractController extends Controller
'working_hours_per_day' => trim((string) $request->post('working_hours_per_day', '8')), 'working_hours_per_day' => trim((string) $request->post('working_hours_per_day', '8')),
'notice_period_months' => (int) $request->post('notice_period_months', 2), 'notice_period_months' => (int) $request->post('notice_period_months', 2),
'basic_salary' => trim((string) $request->post('basic_salary', '0.00')), 'basic_salary' => trim((string) $request->post('basic_salary', '0.00')),
'terms_ar' => trim((string) $request->post('terms_ar', '')) ?: null, 'total_package' => trim((string) $request->post('total_package', '0.00')),
'terms_json' => $termsAr ? json_encode($termsAr, JSON_UNESCAPED_UNICODE) : null,
'notes' => trim((string) $request->post('notes', '')) ?: null, 'notes' => trim((string) $request->post('notes', '')) ?: null,
]; ];
......
...@@ -356,7 +356,7 @@ class EmployeeProfileController extends Controller ...@@ -356,7 +356,7 @@ class EmployeeProfileController extends Controller
'employment_status' => trim((string) $request->post('employment_status', 'active')), 'employment_status' => trim((string) $request->post('employment_status', 'active')),
'probation_end_date' => trim((string) $request->post('probation_end_date', '')) ?: null, 'probation_end_date' => trim((string) $request->post('probation_end_date', '')) ?: null,
'basic_salary' => trim((string) $request->post('basic_salary', '0.00')), 'basic_salary' => trim((string) $request->post('basic_salary', '0.00')),
'insurable_salary' => trim((string) $request->post('insurable_salary', '')) ?: null, 'insurable_salary' => trim((string) $request->post('insurable_salary', '0.00')) ?: '0.00',
'insurance_number' => trim((string) $request->post('insurance_number', '')) ?: null, 'insurance_number' => trim((string) $request->post('insurance_number', '')) ?: null,
'bank_name' => trim((string) $request->post('bank_name', '')) ?: null, 'bank_name' => trim((string) $request->post('bank_name', '')) ?: null,
'bank_account_number' => trim((string) $request->post('bank_account_number', '')) ?: null, 'bank_account_number' => trim((string) $request->post('bank_account_number', '')) ?: null,
......
...@@ -11,6 +11,7 @@ use App\Modules\HR\Models\HrPerformanceCycle; ...@@ -11,6 +11,7 @@ use App\Modules\HR\Models\HrPerformanceCycle;
use App\Modules\HR\Models\HrPerformanceReview; use App\Modules\HR\Models\HrPerformanceReview;
use App\Modules\HR\Models\HrEmployeeProfile; use App\Modules\HR\Models\HrEmployeeProfile;
use App\Modules\HR\Models\HrDepartment; use App\Modules\HR\Models\HrDepartment;
use App\Modules\HR\Services\HrNumberGenerator;
class PerformanceController extends Controller class PerformanceController extends Controller
{ {
...@@ -75,12 +76,18 @@ class PerformanceController extends Controller ...@@ -75,12 +76,18 @@ class PerformanceController extends Controller
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$employee = $this->currentEmployee(); $employee = $this->currentEmployee();
$reviewDeadline = trim((string) $request->post('review_deadline', '')) ?: $endDate;
$cycleCode = HrNumberGenerator::generateCycleCode();
$year = (int) date('Y', strtotime($startDate));
$cycleId = $db->insert('hr_performance_cycles', [ $cycleId = $db->insert('hr_performance_cycles', [
'cycle_code' => $cycleCode,
'name_ar' => $name, 'name_ar' => $name,
'year' => $year,
'period_type' => $cycleType, 'period_type' => $cycleType,
'start_date' => $startDate, 'start_date' => $startDate,
'end_date' => $endDate, 'end_date' => $endDate,
'review_deadline' => $reviewDeadline,
'status' => 'draft', 'status' => 'draft',
'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,10 +99,12 @@ class PerformanceController extends Controller ...@@ -92,10 +99,12 @@ class PerformanceController extends Controller
"SELECT id FROM hr_employee_profiles WHERE employment_status = 'active' AND is_archived = 0" "SELECT id FROM hr_employee_profiles WHERE employment_status = 'active' AND is_archived = 0"
); );
$reviewerId = $employee ? (int) $employee->id : 0;
foreach ($activeEmployees as $emp) { foreach ($activeEmployees as $emp) {
$db->insert('hr_performance_reviews', [ $db->insert('hr_performance_reviews', [
'cycle_id' => $cycleId, 'cycle_id' => $cycleId,
'employee_profile_id' => (int) $emp['id'], 'employee_profile_id' => (int) $emp['id'],
'reviewer_id' => $reviewerId,
'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'),
...@@ -119,7 +128,7 @@ class PerformanceController extends Controller ...@@ -119,7 +128,7 @@ class PerformanceController extends Controller
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$stats = $db->selectOne( $stats = $db->selectOne(
"SELECT COUNT(*) as total, "SELECT COUNT(*) as total,
SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed, SUM(CASE WHEN status = 'submitted' THEN 1 ELSE 0 END) as completed,
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending, SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending,
AVG(CASE WHEN overall_rating > 0 THEN overall_rating ELSE NULL END) as avg_rating AVG(CASE WHEN overall_rating > 0 THEN overall_rating ELSE NULL END) as avg_rating
FROM hr_performance_reviews FROM hr_performance_reviews
......
...@@ -38,7 +38,6 @@ class HrContract extends Model ...@@ -38,7 +38,6 @@ class HrContract extends Model
{ {
return [ return [
'draft' => 'مسودة', 'draft' => 'مسودة',
'pending_approval' => 'في انتظار الاعتماد',
'active' => 'ساري', 'active' => 'ساري',
'renewed' => 'تم التجديد', 'renewed' => 'تم التجديد',
'expired' => 'منتهي', 'expired' => 'منتهي',
...@@ -90,9 +89,9 @@ class HrContract extends Model ...@@ -90,9 +89,9 @@ class HrContract extends Model
$where .= ' AND c.status = ?'; $where .= ' AND c.status = ?';
$params[] = $filters['status']; $params[] = $filters['status'];
} }
if (!empty($filters['contract_type'])) { if (!empty($filters['type'])) {
$where .= ' AND c.contract_type = ?'; $where .= ' AND c.contract_type = ?';
$params[] = $filters['contract_type']; $params[] = $filters['type'];
} }
$countRow = $db->selectOne( $countRow = $db->selectOne(
......
...@@ -55,7 +55,7 @@ class HrEmployeeProfile extends Model ...@@ -55,7 +55,7 @@ class HrEmployeeProfile extends Model
'full_time' => 'دوام كامل', 'full_time' => 'دوام كامل',
'part_time' => 'دوام جزئي', 'part_time' => 'دوام جزئي',
'contract' => 'عقد مؤقت', 'contract' => 'عقد مؤقت',
'seasonal' => 'موسمي', 'temporary' => 'مؤقت',
]; ];
} }
......
...@@ -191,9 +191,12 @@ final class AttendanceService ...@@ -191,9 +191,12 @@ final class AttendanceService
$daysInMonth = (int) date('t', strtotime($startDate)); $daysInMonth = (int) date('t', strtotime($startDate));
$workingDays = 0; $workingDays = 0;
$profile = HrEmployeeProfile::find($profileId);
$religion = $profile->religion ?? null;
for ($d = 1; $d <= $daysInMonth; $d++) { for ($d = 1; $d <= $daysInMonth; $d++) {
$date = sprintf('%04d-%02d-%02d', $year, $month, $d); $date = sprintf('%04d-%02d-%02d', $year, $month, $d);
if (!self::isRestDay($profileId, $date) && !HrHoliday::isHoliday($date)) { if (!self::isRestDay($profileId, $date) && !HrHoliday::isHoliday($date, $religion)) {
$workingDays++; $workingDays++;
} }
} }
......
...@@ -57,7 +57,8 @@ final class ContractService ...@@ -57,7 +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_package' => $data['total_package'] ?? '0.00', 'total_package' => $data['total_package'] ?? $data['basic_salary'] ?? '0.00',
'terms_json' => !empty($data['terms_ar']) ? json_encode($data['terms_ar'], JSON_UNESCAPED_UNICODE) : null,
'renewal_of_contract_id' => $data['renewal_of_contract_id'] ?? null, 'renewal_of_contract_id' => $data['renewal_of_contract_id'] ?? null,
'status' => 'draft', 'status' => 'draft',
'notes' => $data['notes'] ?? null, 'notes' => $data['notes'] ?? null,
......
...@@ -46,6 +46,26 @@ final class HrNumberGenerator ...@@ -46,6 +46,26 @@ final class HrNumberGenerator
return sprintf('PAY-%04d-%02d', $year, $month); return sprintf('PAY-%04d-%02d', $year, $month);
} }
public static function generateCycleCode(): string
{
$db = App::getInstance()->db();
$year = date('Y');
$prefix = 'PC-' . $year . '-';
$last = $db->selectOne(
"SELECT cycle_code FROM hr_performance_cycles WHERE cycle_code LIKE ? ORDER BY id DESC LIMIT 1",
[$prefix . '%']
);
if ($last) {
$parts = explode('-', $last['cycle_code']);
$seq = (int) end($parts) + 1;
} else {
$seq = 1;
}
return $prefix . str_pad((string) $seq, 3, '0', STR_PAD_LEFT);
}
public static function generateLoanReference(): string public static function generateLoanReference(): string
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
...@@ -53,9 +73,15 @@ final class HrNumberGenerator ...@@ -53,9 +73,15 @@ final class HrNumberGenerator
$prefix = 'LN-' . $year . '-'; $prefix = 'LN-' . $year . '-';
$last = $db->selectOne( $last = $db->selectOne(
"SELECT id FROM hr_employee_loans ORDER BY id DESC LIMIT 1" "SELECT loan_number FROM hr_employee_loans WHERE loan_number LIKE ? ORDER BY id DESC LIMIT 1",
[$prefix . '%']
); );
$seq = ($last ? (int) $last['id'] : 0) + 1; if ($last) {
$parts = explode('-', $last['loan_number']);
$seq = (int) end($parts) + 1;
} else {
$seq = 1;
}
return $prefix . str_pad((string) $seq, 5, '0', STR_PAD_LEFT); return $prefix . str_pad((string) $seq, 5, '0', STR_PAD_LEFT);
} }
......
...@@ -310,10 +310,13 @@ final class LeaveService ...@@ -310,10 +310,13 @@ final class LeaveService
$end = new \DateTime($endDate); $end = new \DateTime($endDate);
$days = 0; $days = 0;
$profile = HrEmployeeProfile::find($profileId);
$religion = $profile ? $profile->religion : null;
$current = clone $start; $current = clone $start;
while ($current <= $end) { while ($current <= $end) {
$dateStr = $current->format('Y-m-d'); $dateStr = $current->format('Y-m-d');
if (!AttendanceService::isRestDay($profileId, $dateStr) && !HrHoliday::isHoliday($dateStr)) { if (!AttendanceService::isRestDay($profileId, $dateStr) && !HrHoliday::isHoliday($dateStr, $religion)) {
$days++; $days++;
} }
$current->modify('+1 day'); $current->modify('+1 day');
......
...@@ -8,6 +8,7 @@ use App\Core\EventBus; ...@@ -8,6 +8,7 @@ use App\Core\EventBus;
use App\Core\Logger; use App\Core\Logger;
use App\Modules\HR\Models\HrEmployeeLoan; use App\Modules\HR\Models\HrEmployeeLoan;
use App\Modules\HR\Models\HrLoanInstallment; use App\Modules\HR\Models\HrLoanInstallment;
use App\Modules\HR\Services\HrNumberGenerator;
/** /**
* Loan & Salary Advance Service * Loan & Salary Advance Service
...@@ -43,10 +44,13 @@ final class LoanService ...@@ -43,10 +44,13 @@ final class LoanService
$totalScheduled = bcmul($installmentAmount, (string) $numInstallments, 2); $totalScheduled = bcmul($installmentAmount, (string) $numInstallments, 2);
$lastInstallmentAdj = bcsub($loanAmount, bcmul($installmentAmount, (string) ($numInstallments - 1), 2), 2); $lastInstallmentAdj = bcsub($loanAmount, bcmul($installmentAmount, (string) ($numInstallments - 1), 2), 2);
$loanNumber = HrNumberGenerator::generateLoanReference();
$db->beginTransaction(); $db->beginTransaction();
try { try {
$loanId = $db->insert('hr_employee_loans', [ $loanId = $db->insert('hr_employee_loans', [
'employee_profile_id' => $profileId, 'employee_profile_id' => $profileId,
'loan_number' => $loanNumber,
'loan_type' => $data['loan_type'] ?? 'salary_advance', 'loan_type' => $data['loan_type'] ?? 'salary_advance',
'loan_amount' => $loanAmount, 'loan_amount' => $loanAmount,
'installment_amount' => $installmentAmount, 'installment_amount' => $installmentAmount,
......
...@@ -319,9 +319,13 @@ final class PayrollCalculationService ...@@ -319,9 +319,13 @@ final class PayrollCalculationService
'working_days' => $workingDays, 'working_days' => $workingDays,
], JSON_UNESCAPED_UNICODE); ], JSON_UNESCAPED_UNICODE);
$db->beginTransaction();
try {
$runId = $db->insert('hr_payroll_runs', [ $runId = $db->insert('hr_payroll_runs', [
'period_id' => $periodId, 'period_id' => $periodId,
'employee_profile_id' => $profileId, 'employee_profile_id' => $profileId,
'employee_number' => $profile->employee_number ?? '',
'basic_salary' => $basicSalary, 'basic_salary' => $basicSalary,
'gross_earnings' => $grossEarnings, 'gross_earnings' => $grossEarnings,
'total_allowances' => $totalAllowances, 'total_allowances' => $totalAllowances,
...@@ -401,6 +405,12 @@ final class PayrollCalculationService ...@@ -401,6 +405,12 @@ final class PayrollCalculationService
LoanService::processInstallmentDeduction((int) $inst['loan_id'], $inst['amount'], $runId); LoanService::processInstallmentDeduction((int) $inst['loan_id'], $inst['amount'], $runId);
} }
$db->commit();
} catch (\Throwable $e) {
$db->rollBack();
throw $e;
}
return [ return [
'success' => true, 'success' => true,
'run_id' => $runId, 'run_id' => $runId,
......
...@@ -51,9 +51,14 @@ ...@@ -51,9 +51,14 @@
<label style="display:block;margin-bottom:4px;font-size:13px;">الراتب الأساسي</label> <label style="display:block;margin-bottom:4px;font-size:13px;">الراتب الأساسي</label>
<input type="text" name="basic_salary" value="<?= e(old('basic_salary') ?? ($isEdit ? $contract->basic_salary : ($profile ? $profile->basic_salary : '0.00'))) ?>" class="form-control"> <input type="text" name="basic_salary" value="<?= e(old('basic_salary') ?? ($isEdit ? $contract->basic_salary : ($profile ? $profile->basic_salary : '0.00'))) ?>" class="form-control">
</div> </div>
<div>
<label style="display:block;margin-bottom:4px;font-size:13px;">إجمالي الحزمة</label>
<input type="text" name="total_package" value="<?= e(old('total_package') ?? ($isEdit ? $contract->total_package : ($profile ? $profile->basic_salary : '0.00'))) ?>" class="form-control">
<small style="color:#6B7280;">الراتب الأساسي + البدلات</small>
</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="terms_ar" class="form-control" rows="4"><?= e(old('terms_ar') ?? ($isEdit ? $contract->terms_ar : '')) ?></textarea> <textarea name="terms_ar" class="form-control" rows="4"><?= e(old('terms_ar') ?? ($isEdit ? json_decode($contract->terms_json ?? 'null', true) ?? '' : '')) ?></textarea>
</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>
......
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
<?php $statusColors = ['draft' => '#E5E7EB;color:#374151', 'active' => '#DEF7EC;color:#03543F', 'expired' => '#FEF3C7;color:#92400E', 'terminated' => '#FDE8E8;color:#9B1C1C', 'renewed' => '#DBEAFE;color:#1E40AF']; ?> <?php $statusColors = ['draft' => '#E5E7EB;color:#374151', 'active' => '#DEF7EC;color:#03543F', 'expired' => '#FEF3C7;color:#92400E', 'terminated' => '#FDE8E8;color:#9B1C1C', 'renewed' => '#DBEAFE;color:#1E40AF']; ?>
<tr> <tr>
<td><a href="/hr/contracts/<?= (int) $c['id'] ?>"><?= e($c['contract_number']) ?></a></td> <td><a href="/hr/contracts/<?= (int) $c['id'] ?>"><?= e($c['contract_number']) ?></a></td>
<td><?= e(($c['first_name_ar'] ?? '') . ' ' . ($c['last_name_ar'] ?? '')) ?></td> <td><?= e($c['full_name_ar'] ?? '') ?></td>
<td><?= $c['contract_type'] === 'definite' ? 'محدد المدة' : 'غير محدد' ?></td> <td><?= $c['contract_type'] === 'definite' ? 'محدد المدة' : 'غير محدد' ?></td>
<td><?= e($c['start_date']) ?></td> <td><?= e($c['start_date']) ?></td>
<td><?= e($c['end_date'] ?? '-') ?></td> <td><?= e($c['end_date'] ?? '-') ?></td>
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
<div><span style="color:#6B7280;font-size:13px;">ساعات العمل</span><div><?= e($contract->working_hours_per_day) ?> ساعة/يوم</div></div> <div><span style="color:#6B7280;font-size:13px;">ساعات العمل</span><div><?= e($contract->working_hours_per_day) ?> ساعة/يوم</div></div>
<div><span style="color:#6B7280;font-size:13px;">فترة الإنذار</span><div><?= (int) $contract->notice_period_months ?> أشهر</div></div> <div><span style="color:#6B7280;font-size:13px;">فترة الإنذار</span><div><?= (int) $contract->notice_period_months ?> أشهر</div></div>
<div><span style="color:#6B7280;font-size:13px;">الراتب الأساسي</span><div style="font-weight:600;"><?= number_format((float) $contract->basic_salary, 2) ?> ج.م</div></div> <div><span style="color:#6B7280;font-size:13px;">الراتب الأساسي</span><div style="font-weight:600;"><?= number_format((float) $contract->basic_salary, 2) ?> ج.م</div></div>
<div><span style="color:#6B7280;font-size:13px;">إجمالي الحزمة</span><div style="font-weight:600;"><?= number_format((float) $contract->total_package, 2) ?> ج.م</div></div>
</div> </div>
</div> </div>
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
</select> </select>
</div> </div>
<div><label style="display:block;margin-bottom:4px;font-size:13px;"><input type="checkbox" name="is_recurring" value="1" <?= (old('is_recurring') ?? ($isEdit ? $holiday->is_recurring : 0)) ? 'checked' : '' ?>> متكررة سنوياً</label></div> <div><label style="display:block;margin-bottom:4px;font-size:13px;"><input type="checkbox" name="is_recurring" value="1" <?= (old('is_recurring') ?? ($isEdit ? $holiday->is_recurring : 0)) ? 'checked' : '' ?>> متكررة سنوياً</label></div>
<div><label style="display:block;margin-bottom:4px;font-size:13px;"><input type="checkbox" name="is_active" value="1" <?= (old('is_active') ?? ($isEdit ? $holiday->is_active : 1)) ? 'checked' : '' ?>> فعالة</label></div>
</div> </div>
</div> </div>
<div style="display:flex;gap:10px;justify-content:flex-end;"> <div style="display:flex;gap:10px;justify-content:flex-end;">
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
<label style="display:block;margin-bottom:4px;font-size:13px;">الحد الأقصى للراتب</label> <label style="display:block;margin-bottom:4px;font-size:13px;">الحد الأقصى للراتب</label>
<input type="text" name="max_salary" value="<?= e(old('max_salary') ?? ($isEdit ? $jobTitle->max_salary : '')) ?>" class="form-control" placeholder="0.00"> <input type="text" name="max_salary" value="<?= e(old('max_salary') ?? ($isEdit ? $jobTitle->max_salary : '')) ?>" class="form-control" placeholder="0.00">
</div> </div>
<div><label style="display:block;margin-bottom:4px;font-size:13px;"><input type="checkbox" name="is_active" value="1" <?= (old('is_active') ?? ($isEdit ? $jobTitle->is_active : 1)) ? 'checked' : '' ?>> فعال</label></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_ar" class="form-control" rows="3"><?= e(old('description_ar') ?? ($isEdit ? $jobTitle->description_ar : '')) ?></textarea> <textarea name="description_ar" class="form-control" rows="3"><?= e(old('description_ar') ?? ($isEdit ? $jobTitle->description_ar : '')) ?></textarea>
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
<?php $__template->section('title'); ?>دورات التقييم<?php $__template->endSection(); ?> <?php $__template->section('title'); ?>دورات التقييم<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?><a href="/hr/performance" class="btn btn-secondary">رجوع</a><?php $__template->endSection(); ?> <?php $__template->section('page_actions'); ?><a href="/hr/performance" class="btn btn-secondary">رجوع</a><?php $__template->endSection(); ?>
<?php $__template->section('content'); ?> <?php $__template->section('content'); ?>
<?php $csl = ['draft'=>'مسودة','active'=>'نشطة','completed'=>'مكتملة','closed'=>'مغلقة']; ?> <?php $csl = ['draft'=>'مسودة','active'=>'نشطة','review'=>'تحت المراجعة','closed'=>'مغلقة']; ?>
<?php $csc = ['draft'=>'#FEF3C7;color:#92400E','active'=>'#D1FAE5;color:#065F46','completed'=>'#DBEAFE;color:#1E40AF','closed'=>'#E5E7EB;color:#374151']; ?> <?php $csc = ['draft'=>'#FEF3C7;color:#92400E','active'=>'#D1FAE5;color:#065F46','review'=>'#DBEAFE;color:#1E40AF','closed'=>'#E5E7EB;color:#374151']; ?>
<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>
...@@ -12,10 +12,10 @@ ...@@ -12,10 +12,10 @@
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-bottom:12px;"> <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-bottom:12px;">
<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') ?? '') ?>" class="form-control" placeholder="مثال: تقييم الأداء 2026" 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') ?? '') ?>" class="form-control" placeholder="مثال: تقييم الأداء 2026" required></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="cycle_type" class="form-control"> <select name="period_type" class="form-control">
<option value="annual" <?= old('cycle_type') === 'annual' ? 'selected' : '' ?>>سنوي</option> <option value="annual" <?= old('period_type') === 'annual' ? 'selected' : '' ?>>سنوي</option>
<option value="semi_annual" <?= old('cycle_type') === 'semi_annual' ? 'selected' : '' ?>>نصف سنوي</option> <option value="semi_annual" <?= old('period_type') === 'semi_annual' ? 'selected' : '' ?>>نصف سنوي</option>
<option value="quarterly" <?= old('cycle_type') === 'quarterly' ? 'selected' : '' ?>>ربع سنوي</option> <option value="quarterly" <?= old('period_type') === 'quarterly' ? 'selected' : '' ?>>ربع سنوي</option>
</select> </select>
</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>
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
<?php foreach ($cycles as $c): ?> <?php foreach ($cycles as $c): ?>
<tr> <tr>
<td style="font-weight:600;"><?= e($c['name_ar']) ?></td> <td style="font-weight:600;"><?= e($c['name_ar']) ?></td>
<td><?= $c['cycle_type'] === 'annual' ? 'سنوي' : ($c['cycle_type'] === 'semi_annual' ? 'نصف سنوي' : 'ربع سنوي') ?></td> <td><?= $c['period_type'] === 'annual' ? 'سنوي' : ($c['period_type'] === 'semi_annual' ? 'نصف سنوي' : 'ربع سنوي') ?></td>
<td><?= e($c['start_date']) ?> - <?= e($c['end_date']) ?></td> <td><?= e($c['start_date']) ?> - <?= e($c['end_date']) ?></td>
<td><?= (int) ($c['review_count'] ?? 0) ?></td> <td><?= (int) ($c['review_count'] ?? 0) ?></td>
<td><?php if ($c['avg_rating']): ?><span style="font-weight:600;color:#D97706;"><?= number_format((float) $c['avg_rating'], 1) ?>/5</span><?php else: ?>-<?php endif; ?></td> <td><?php if ($c['avg_rating']): ?><span style="font-weight:600;color:#D97706;"><?= number_format((float) $c['avg_rating'], 1) ?>/5</span><?php else: ?>-<?php endif; ?></td>
......
...@@ -2,15 +2,15 @@ ...@@ -2,15 +2,15 @@
<?php $__template->section('title'); ?>تقييم الأداء<?php $__template->endSection(); ?> <?php $__template->section('title'); ?>تقييم الأداء<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?><a href="/hr/performance/cycles" class="btn btn-secondary">جميع الدورات</a><?php $__template->endSection(); ?> <?php $__template->section('page_actions'); ?><a href="/hr/performance/cycles" class="btn btn-secondary">جميع الدورات</a><?php $__template->endSection(); ?>
<?php $__template->section('content'); ?> <?php $__template->section('content'); ?>
<?php $csl = ['draft'=>'مسودة','active'=>'نشطة','completed'=>'مكتملة','closed'=>'مغلقة']; ?> <?php $csl = ['draft'=>'مسودة','active'=>'نشطة','review'=>'تحت المراجعة','closed'=>'مغلقة']; ?>
<?php $csc = ['draft'=>'#FEF3C7;color:#92400E','active'=>'#D1FAE5;color:#065F46','completed'=>'#DBEAFE;color:#1E40AF','closed'=>'#E5E7EB;color:#374151']; ?> <?php $csc = ['draft'=>'#FEF3C7;color:#92400E','active'=>'#D1FAE5;color:#065F46','review'=>'#DBEAFE;color:#1E40AF','closed'=>'#E5E7EB;color:#374151']; ?>
<?php if ($activeCycle): ?> <?php if ($activeCycle): ?>
<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;display:flex;align-items:center;gap:8px;">الدورة النشطة <span style="display:inline-block;padding:2px 8px;border-radius:9999px;font-size:12px;background:#D1FAE5;color:#065F46;">نشطة</span></h3></div> <div style="padding:16px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;font-size:16px;display:flex;align-items:center;gap:8px;">الدورة النشطة <span style="display:inline-block;padding:2px 8px;border-radius:9999px;font-size:12px;background:#D1FAE5;color:#065F46;">نشطة</span></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;"><?= e($activeCycle['name_ar']) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">اسم الدورة</span><div style="font-weight:600;"><?= e($activeCycle['name_ar']) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">النوع</span><div><?= $activeCycle['cycle_type'] === 'annual' ? 'سنوي' : ($activeCycle['cycle_type'] === 'semi_annual' ? 'نصف سنوي' : 'ربع سنوي') ?></div></div> <div><span style="color:#6B7280;font-size:13px;">النوع</span><div><?= $activeCycle['period_type'] === 'annual' ? 'سنوي' : ($activeCycle['period_type'] === 'semi_annual' ? 'نصف سنوي' : 'ربع سنوي') ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">الفترة</span><div><?= e($activeCycle['start_date']) ?> - <?= e($activeCycle['end_date']) ?></div></div> <div><span style="color:#6B7280;font-size:13px;">الفترة</span><div><?= e($activeCycle['start_date']) ?> - <?= e($activeCycle['end_date']) ?></div></div>
</div> </div>
<div style="padding:0 16px 16px;"><a href="/hr/performance/cycles/<?= (int) $activeCycle['id'] ?>" class="btn btn-primary">عرض التقييمات</a></div> <div style="padding:0 16px 16px;"><a href="/hr/performance/cycles/<?= (int) $activeCycle['id'] ?>" class="btn btn-primary">عرض التقييمات</a></div>
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
<?php foreach ($recentCycles as $c): ?> <?php foreach ($recentCycles as $c): ?>
<tr> <tr>
<td style="font-weight:600;"><?= e($c['name_ar']) ?></td> <td style="font-weight:600;"><?= e($c['name_ar']) ?></td>
<td><?= $c['cycle_type'] === 'annual' ? 'سنوي' : ($c['cycle_type'] === 'semi_annual' ? 'نصف سنوي' : 'ربع سنوي') ?></td> <td><?= $c['period_type'] === 'annual' ? 'سنوي' : ($c['period_type'] === 'semi_annual' ? 'نصف سنوي' : 'ربع سنوي') ?></td>
<td><?= e($c['start_date']) ?> - <?= e($c['end_date']) ?></td> <td><?= e($c['start_date']) ?> - <?= e($c['end_date']) ?></td>
<td><span style="display:inline-block;padding:2px 8px;border-radius:9999px;font-size:12px;background:<?= $csc[$c['status']] ?? '#E5E7EB;color:#374151' ?>;"><?= e($csl[$c['status']] ?? $c['status']) ?></span></td> <td><span style="display:inline-block;padding:2px 8px;border-radius:9999px;font-size:12px;background:<?= $csc[$c['status']] ?? '#E5E7EB;color:#374151' ?>;"><?= e($csl[$c['status']] ?? $c['status']) ?></span></td>
<td><a href="/hr/performance/cycles/<?= (int) $c['id'] ?>" style="color:#2563EB;"><i data-lucide="eye" style="width:16px;height:16px;"></i></a></td> <td><a href="/hr/performance/cycles/<?= (int) $c['id'] ?>" style="color:#2563EB;"><i data-lucide="eye" style="width:16px;height:16px;"></i></a></td>
......
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
<?php $__template->section('title'); ?><?= e($cycle->name_ar) ?><?php $__template->endSection(); ?> <?php $__template->section('title'); ?><?= e($cycle->name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?><a href="/hr/performance/cycles" class="btn btn-secondary">رجوع</a><?php $__template->endSection(); ?> <?php $__template->section('page_actions'); ?><a href="/hr/performance/cycles" class="btn btn-secondary">رجوع</a><?php $__template->endSection(); ?>
<?php $__template->section('content'); ?> <?php $__template->section('content'); ?>
<?php $csl = ['draft'=>'مسودة','active'=>'نشطة','completed'=>'مكتملة','closed'=>'مغلقة']; ?> <?php $csl = ['draft'=>'مسودة','active'=>'نشطة','review'=>'تحت المراجعة','closed'=>'مغلقة']; ?>
<?php $csc = ['draft'=>'#FEF3C7;color:#92400E','active'=>'#D1FAE5;color:#065F46','completed'=>'#DBEAFE;color:#1E40AF','closed'=>'#E5E7EB;color:#374151']; ?> <?php $csc = ['draft'=>'#FEF3C7;color:#92400E','active'=>'#D1FAE5;color:#065F46','review'=>'#DBEAFE;color:#1E40AF','closed'=>'#E5E7EB;color:#374151']; ?>
<?php $rsl = ['pending'=>'بانتظار التقييم','completed'=>'مكتمل','acknowledged'=>'تم الإقرار']; ?> <?php $rsl = ['pending'=>'بانتظار التقييم','in_progress'=>'قيد التقييم','submitted'=>'تم التقديم','acknowledged'=>'تم الإقرار']; ?>
<?php $rsc = ['pending'=>'#FEF3C7;color:#92400E','completed'=>'#D1FAE5;color:#065F46','acknowledged'=>'#DBEAFE;color:#1E40AF']; ?> <?php $rsc = ['pending'=>'#FEF3C7;color:#92400E','in_progress'=>'#FEF3C7;color:#92400E','submitted'=>'#D1FAE5;color:#065F46','acknowledged'=>'#DBEAFE;color:#1E40AF']; ?>
<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="margin-top:4px;"><span style="display:inline-block;padding:2px 8px;border-radius:9999px;font-size:12px;background:<?= $csc[$cycle->status] ?? '#E5E7EB;color:#374151' ?>;"><?= e($csl[$cycle->status] ?? $cycle->status) ?></span></div></div> <div class="card" style="padding:16px;text-align:center;"><div style="font-size:13px;color:#6B7280;">الحالة</div><div style="margin-top:4px;"><span style="display:inline-block;padding:2px 8px;border-radius:9999px;font-size:12px;background:<?= $csc[$cycle->status] ?? '#E5E7EB;color:#374151' ?>;"><?= e($csl[$cycle->status] ?? $cycle->status) ?></span></div></div>
...@@ -38,7 +38,7 @@ $pct = $total > 0 ? round($completed / $total * 100) : 0; ...@@ -38,7 +38,7 @@ $pct = $total > 0 ? round($completed / $total * 100) : 0;
<?php else: ?> <?php else: ?>
<?php foreach ($reviews as $r): ?> <?php foreach ($reviews as $r): ?>
<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['full_name_ar'] ?? '') ?></a></td>
<td><?= e($r['department_name'] ?? '-') ?></td> <td><?= e($r['department_name'] ?? '-') ?></td>
<td><?php if ((float) ($r['overall_rating'] ?? 0) > 0): ?> <td><?php if ((float) ($r['overall_rating'] ?? 0) > 0): ?>
<?php $rating = (float) $r['overall_rating']; $color = $rating >= 4 ? '#059669' : ($rating >= 3 ? '#D97706' : '#DC2626'); ?> <?php $rating = (float) $r['overall_rating']; $color = $rating >= 4 ? '#059669' : ($rating >= 3 ? '#D97706' : '#DC2626'); ?>
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
<div><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 ? $structure->code : '')) ?>" 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="code" value="<?= e(old('code') ?? ($isEdit ? $structure->code : '')) ?>" class="form-control" required></div>
<div><label style="display:block;margin-bottom:4px;font-size:13px;">الوصف</label><input type="text" name="description" value="<?= e(old('description') ?? ($isEdit ? $structure->description : '')) ?>" class="form-control"></div> <div><label style="display:block;margin-bottom:4px;font-size:13px;">الوصف</label><input type="text" name="description" value="<?= e(old('description') ?? ($isEdit ? $structure->description : '')) ?>" class="form-control"></div>
<div><label style="display:block;margin-bottom:4px;font-size:13px;"><input type="checkbox" name="is_default" value="1" <?= (old('is_default') ?? ($isEdit ? $structure->is_default : 0)) ? 'checked' : '' ?>> هيكل افتراضي</label></div> <div><label style="display:block;margin-bottom:4px;font-size:13px;"><input type="checkbox" name="is_default" value="1" <?= (old('is_default') ?? ($isEdit ? $structure->is_default : 0)) ? 'checked' : '' ?>> هيكل افتراضي</label></div>
<div><label style="display:block;margin-bottom:4px;font-size:13px;"><input type="checkbox" name="is_active" value="1" <?= (old('is_active') ?? ($isEdit ? $structure->is_active : 1)) ? 'checked' : '' ?>> فعال</label></div>
</div> </div>
</div> </div>
<div style="display:flex;gap:10px;justify-content:flex-end;"> <div style="display:flex;gap:10px;justify-content:flex-end;">
......
...@@ -66,12 +66,14 @@ class HrContractExpiryJob ...@@ -66,12 +66,14 @@ class HrContractExpiryJob
$employeeName = $c['first_name_ar'] . ' ' . $c['last_name_ar']; $employeeName = $c['first_name_ar'] . ' ' . $c['last_name_ar'];
// Insert notification for HR managers // Insert notification for HR managers
$this->db->insert('notifications', [ $this->db->insert('notification_queue', [
'type' => 'hr_contract_expiry', 'type' => 'system',
'title' => 'عقد قارب على الانتهاء', 'recipient_type' => 'employee',
'recipient_id' => 0,
'subject' => 'عقد قارب على الانتهاء',
'message' => "عقد الموظف {$employeeName} سينتهي خلال {$daysRemaining} يوم (تاريخ الانتهاء: {$c['end_date']})", 'message' => "عقد الموظف {$employeeName} سينتهي خلال {$daysRemaining} يوم (تاريخ الانتهاء: {$c['end_date']})",
'link' => '/hr/contracts/' . (int) $c['id'], 'data_json' => json_encode(['hr_type' => 'hr_contract_expiry', 'link' => '/hr/contracts/' . (int) $c['id']], JSON_UNESCAPED_UNICODE),
'is_read' => 0, 'status' => 'pending',
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
]); ]);
$alerted++; $alerted++;
......
...@@ -59,21 +59,23 @@ class HrDocumentExpiryJob ...@@ -59,21 +59,23 @@ class HrDocumentExpiryJob
$docTypeName = $docTypes[$doc['document_type']] ?? $doc['document_type']; $docTypeName = $docTypes[$doc['document_type']] ?? $doc['document_type'];
// Only alert once per week (check if similar notification exists in last 7 days) // Only alert once per week (check if similar notification exists in last 7 days)
$link = '/hr/documents/employee/' . (int) $doc['employee_profile_id'];
$recentAlert = $this->db->selectOne( $recentAlert = $this->db->selectOne(
"SELECT id FROM notifications "SELECT id FROM notification_queue
WHERE type = 'hr_document_expiry' WHERE data_json LIKE ?
AND link = ?
AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)", AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)",
['/hr/documents/employee/' . (int) $doc['employee_profile_id']] ['%hr_document_expiry%' . $link . '%']
); );
if (!$recentAlert) { if (!$recentAlert) {
$this->db->insert('notifications', [ $this->db->insert('notification_queue', [
'type' => 'hr_document_expiry', 'type' => 'system',
'title' => 'مستند قارب على الانتهاء', 'recipient_type' => 'employee',
'recipient_id' => 0,
'subject' => 'مستند قارب على الانتهاء',
'message' => "{$docTypeName} للموظف {$employeeName} سينتهي خلال {$daysRemaining} يوم (تاريخ الانتهاء: {$doc['expiry_date']})", 'message' => "{$docTypeName} للموظف {$employeeName} سينتهي خلال {$daysRemaining} يوم (تاريخ الانتهاء: {$doc['expiry_date']})",
'link' => '/hr/documents/employee/' . (int) $doc['employee_profile_id'], 'data_json' => json_encode(['hr_type' => 'hr_document_expiry', 'link' => $link], JSON_UNESCAPED_UNICODE),
'is_read' => 0, 'status' => 'pending',
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
]); ]);
$alerted++; $alerted++;
......
...@@ -48,12 +48,14 @@ class HrLoanDeductionReminderJob ...@@ -48,12 +48,14 @@ class HrLoanDeductionReminderJob
$totalAmount = bcadd($totalAmount, (string) $inst['amount'], 2); $totalAmount = bcadd($totalAmount, (string) $inst['amount'], 2);
} }
$this->db->insert('notifications', [ $this->db->insert('notification_queue', [
'type' => 'hr_loan_deduction', 'type' => 'system',
'title' => 'تذكير: أقساط السلف', 'recipient_type' => 'employee',
'recipient_id' => 0,
'subject' => 'تذكير: أقساط السلف',
'message' => "يوجد " . count($pendingInstallments) . " قسط سلفة مستحق هذا الشهر بإجمالي " . number_format((float) $totalAmount, 2) . " ج.م. تأكد من خصمها في الرواتب.", 'message' => "يوجد " . count($pendingInstallments) . " قسط سلفة مستحق هذا الشهر بإجمالي " . number_format((float) $totalAmount, 2) . " ج.م. تأكد من خصمها في الرواتب.",
'link' => '/hr/loans', 'data_json' => json_encode(['hr_type' => 'hr_loan_deduction', 'link' => '/hr/loans'], JSON_UNESCAPED_UNICODE),
'is_read' => 0, 'status' => 'pending',
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
]); ]);
$reminded = count($pendingInstallments); $reminded = count($pendingInstallments);
...@@ -73,12 +75,14 @@ class HrLoanDeductionReminderJob ...@@ -73,12 +75,14 @@ class HrLoanDeductionReminderJob
$overdue = count($overdueInstallments); $overdue = count($overdueInstallments);
if ($overdue > 0) { if ($overdue > 0) {
$this->db->insert('notifications', [ $this->db->insert('notification_queue', [
'type' => 'hr_loan_overdue', 'type' => 'system',
'title' => 'تنبيه: أقساط سلف متأخرة', 'recipient_type' => 'employee',
'recipient_id' => 0,
'subject' => 'تنبيه: أقساط سلف متأخرة',
'message' => "يوجد {$overdue} قسط سلفة متأخر عن موعد السداد. يرجى مراجعة حالة السلف.", 'message' => "يوجد {$overdue} قسط سلفة متأخر عن موعد السداد. يرجى مراجعة حالة السلف.",
'link' => '/hr/loans', 'data_json' => json_encode(['hr_type' => 'hr_loan_overdue', 'link' => '/hr/loans'], JSON_UNESCAPED_UNICODE),
'is_read' => 0, 'status' => 'pending',
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
]); ]);
} }
......
...@@ -38,34 +38,40 @@ class HrPayrollReminderJob ...@@ -38,34 +38,40 @@ class HrPayrollReminderJob
if (!$period) { if (!$period) {
// No payroll period created yet // No payroll period created yet
$this->db->insert('notifications', [ $this->db->insert('notification_queue', [
'type' => 'hr_payroll_reminder', 'type' => 'system',
'title' => 'تذكير: فترة الرواتب', 'recipient_type' => 'employee',
'recipient_id' => 0,
'subject' => 'تذكير: فترة الرواتب',
'message' => "لم يتم إنشاء فترة رواتب شهر {$monthName} {$year} بعد. يرجى إنشاء الفترة وحساب الرواتب.", 'message' => "لم يتم إنشاء فترة رواتب شهر {$monthName} {$year} بعد. يرجى إنشاء الفترة وحساب الرواتب.",
'link' => '/hr/payroll', 'data_json' => json_encode(['hr_type' => 'hr_payroll_reminder', 'link' => '/hr/payroll'], JSON_UNESCAPED_UNICODE),
'is_read' => 0, 'status' => 'pending',
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
]); ]);
$reminded++; $reminded++;
} elseif ($period['status'] === 'open') { } elseif ($period['status'] === 'open') {
// Period exists but not yet calculated // Period exists but not yet calculated
$this->db->insert('notifications', [ $this->db->insert('notification_queue', [
'type' => 'hr_payroll_reminder', 'type' => 'system',
'title' => 'تذكير: حساب الرواتب', 'recipient_type' => 'employee',
'recipient_id' => 0,
'subject' => 'تذكير: حساب الرواتب',
'message' => "فترة رواتب شهر {$monthName} {$year} مفتوحة ولم يتم حساب الرواتب بعد.", 'message' => "فترة رواتب شهر {$monthName} {$year} مفتوحة ولم يتم حساب الرواتب بعد.",
'link' => '/hr/payroll/' . (int) $period['id'], 'data_json' => json_encode(['hr_type' => 'hr_payroll_reminder', 'link' => '/hr/payroll/' . (int) $period['id']], JSON_UNESCAPED_UNICODE),
'is_read' => 0, 'status' => 'pending',
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
]); ]);
$reminded++; $reminded++;
} elseif ($period['status'] === 'calculated') { } elseif ($period['status'] === 'calculated') {
// Calculated but not approved // Calculated but not approved
$this->db->insert('notifications', [ $this->db->insert('notification_queue', [
'type' => 'hr_payroll_reminder', 'type' => 'system',
'title' => 'تذكير: اعتماد الرواتب', 'recipient_type' => 'employee',
'recipient_id' => 0,
'subject' => 'تذكير: اعتماد الرواتب',
'message' => "رواتب شهر {$monthName} {$year} تم حسابها وبانتظار الاعتماد.", 'message' => "رواتب شهر {$monthName} {$year} تم حسابها وبانتظار الاعتماد.",
'link' => '/hr/payroll/' . (int) $period['id'], 'data_json' => json_encode(['hr_type' => 'hr_payroll_reminder', 'link' => '/hr/payroll/' . (int) $period['id']], JSON_UNESCAPED_UNICODE),
'is_read' => 0, 'status' => 'pending',
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
]); ]);
$reminded++; $reminded++;
......
...@@ -43,12 +43,14 @@ class HrProbationEndJob ...@@ -43,12 +43,14 @@ class HrProbationEndJob
$daysRemaining = (int) $emp['days_remaining']; $daysRemaining = (int) $emp['days_remaining'];
$employeeName = $emp['first_name_ar'] . ' ' . $emp['last_name_ar']; $employeeName = $emp['first_name_ar'] . ' ' . $emp['last_name_ar'];
$this->db->insert('notifications', [ $this->db->insert('notification_queue', [
'type' => 'hr_probation_end', 'type' => 'system',
'title' => 'انتهاء فترة اختبار', 'recipient_type' => 'employee',
'recipient_id' => 0,
'subject' => 'انتهاء فترة اختبار',
'message' => "فترة اختبار الموظف {$employeeName} ستنتهي خلال {$daysRemaining} يوم (تاريخ الانتهاء: {$emp['probation_end_date']})", 'message' => "فترة اختبار الموظف {$employeeName} ستنتهي خلال {$daysRemaining} يوم (تاريخ الانتهاء: {$emp['probation_end_date']})",
'link' => '/hr/employees/' . (int) $emp['id'], 'data_json' => json_encode(['hr_type' => 'hr_probation_end', 'link' => '/hr/employees/' . (int) $emp['id']], JSON_UNESCAPED_UNICODE),
'is_read' => 0, 'status' => 'pending',
'created_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s'),
]); ]);
$alerted++; $alerted++;
......
This diff is collapsed.
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