@php use App\Models\LeaveType; use Carbon\Carbon; /* |-------------------------------------------------------------------------- | 1) إعداد تواريخ العقد والإشعار |-------------------------------------------------------------------------- */ $contractEndDate = $employee->contract_end_date ? Carbon::parse($employee->contract_end_date) : null; $notificationPeriod = $employee->notification_period ?? 30; $notificationDate = $contractEndDate ? $contractEndDate->copy()->subDays($notificationPeriod) : null; $today = Carbon::today(); $contractStart = Carbon::parse($employee->start_date); // نهاية الاستحقاق = min(اليوم, نهاية العقد إن وجدت) $eligibilityEnd = $today->copy(); if ($contractEndDate && $contractEndDate->lessThan($eligibilityEnd)) { $eligibilityEnd = $contractEndDate->copy(); } /* |-------------------------------------------------------------------------- | 2) دالة حساب الاستحقاق السنوي "تراكمي" من بداية العقد إلى اليوم | - نحسب التراكم عبر السنوات كاملة/جزئية (مع مراعاة السنة الكبيسة) | - بدون تكرار توزيع (Proration) مرتين |-------------------------------------------------------------------------- */ $calcAccruedAnnualEntitlement = function (Carbon $start, Carbon $end, float $capPerYear): float { if ($start->gt($end)) return 0.0; $total = 0.0; $cursor = $start->copy(); while ($cursor->lte($end)) { $yearStart = $cursor->copy()->startOfYear(); $yearEnd = $cursor->copy()->endOfYear(); $segmentStart = $cursor->copy()->greaterThan($yearStart) ? $cursor->copy() : $yearStart->copy(); $segmentEnd = $end->copy()->lessThan($yearEnd) ? $end->copy() : $yearEnd->copy(); $daysInYear = $yearStart->isLeapYear() ? 366 : 365; $eligibleDays = $segmentStart->diffInDays($segmentEnd) + 1; // +1 لاحتساب يوم النهاية // استحقاق السنة = capPerYear * (أيام مؤهلة / أيام السنة) $total += ($capPerYear * $eligibleDays) / $daysInYear; // انتقل لأول يوم من السنة التالية $cursor = $yearEnd->copy()->addDay(); } return round($total, 2); }; /* |-------------------------------------------------------------------------- | 3) جلب أنواع الإجازات بحسب الجنس |-------------------------------------------------------------------------- */ $leaveTypes = LeaveType::query() ->where(function ($q) use ($employee) { $q->where('gender_applicable', 'Both') ->orWhere('gender_applicable', $employee->gender); }) ->orderBy('name') ->get(); /* |-------------------------------------------------------------------------- | 4) أرصدة لكل نوع + تجميع السنوي (تراكمي) |-------------------------------------------------------------------------- */ $leaveBalances = []; $annualTotals = ['entitlement' => 0.0, 'used' => 0.0, 'remaining' => 0.0]; foreach ($leaveTypes as $type) { // المستخدم من هذا النوع خلال "فترة العقد حتى اليوم" (تراكمي) $usedDays = (float) $employee->leaves ->where('status', 'approved') ->where('leave_type_id', $type->id) ->filter(function ($lv) use ($contractStart, $eligibilityEnd) { if (empty($lv->start_date)) return false; $sd = Carbon::parse($lv->start_date); return $sd->between($contractStart, $eligibilityEnd, true); }) ->sum('duration'); // حدود النوع $maxDaysConfigured = is_null($type->duration_max_days) ? null : (float) $type->duration_max_days; // cap سنوي افتراضي 21 إذا لم يحدد $cap = $type->frequency === 'ANNUAL' ? ($maxDaysConfigured ?? 21.0) : ($maxDaysConfigured ?? 0.0); $entitlement = 0.0; $remaining = 0.0; switch ($type->frequency) { case 'ANNUAL': // استحقاق سنوي تراكمي من بداية العقد إلى اليوم (بدون تكرار توزيع) $entitlement = $calcAccruedAnnualEntitlement($contractStart, $eligibilityEnd, (float) $cap); $remaining = max(0, round($entitlement - $usedDays, 2)); $annualTotals['entitlement'] += $entitlement; break; case 'AGREEMENT': case 'ONCE_LIFETIME': case 'ONCE_EVENT': $entitlement = (float) $cap; $remaining = max(0, round($entitlement - $usedDays, 2)); break; case 'MULTIPLE_EVENT': $entitlement = (float) $cap; $remaining = $entitlement; // Per event limit break; default: $entitlement = (float) $cap; $remaining = max(0, round($entitlement - $usedDays, 2)); } $leaveBalances[] = [ 'id' => $type->id, 'name' => $type->name, 'frequency' => $type->frequency, 'pay_rule' => $type->pay_rule, 'gender' => $type->gender_applicable, 'min_days' => $type->duration_min_days, 'max_days' => $type->duration_max_days, 'entitlement' => $entitlement, 'used' => $usedDays, 'remaining' => $remaining, ]; } /* |-------------------------------------------------------------------------- | 5) ملخص السنوي بشكل مركزي (تراكمي من بداية العقد إلى اليوم) |-------------------------------------------------------------------------- */ $annualTypeIds = collect($leaveTypes)->where('frequency', 'ANNUAL')->pluck('id'); $usedDaysAllAnnual = (float) $employee->leaves ->where('status', 'approved') ->whereIn('leave_type_id', $annualTypeIds->all()) ->filter(function ($lv) use ($contractStart, $eligibilityEnd) { if (empty($lv->start_date)) return false; $sd = Carbon::parse($lv->start_date); return $sd->between($contractStart, $eligibilityEnd, true); }) ->sum('duration'); $totalEntitlement = round($annualTotals['entitlement'], 2); $usedDaysAllAnnual = round($usedDaysAllAnnual, 2); $remainingLeaveDays = max(0, round($totalEntitlement - $usedDaysAllAnnual, 2)); $annualTotals['used'] = $usedDaysAllAnnual; $annualTotals['remaining'] = $remainingLeaveDays; $groupedByFrequency = collect($leaveBalances)->groupBy('frequency'); $freqLabels = [ 'ANNUAL' => __('Annual'), 'AGREEMENT' => __('By Agreement'), 'ONCE_LIFETIME' => __('Once in Lifetime'), 'ONCE_EVENT' => __('Per Event (Once)'), 'MULTIPLE_EVENT' => __('Per Event (Multiple)'), ]; $payRuleColors = [ 'FULL_PAY' => 'bg-green-100 text-green-800', 'PARTIAL_PAY' => 'bg-yellow-100 text-yellow-800', 'NO_PAY' => 'bg-red-100 text-red-800', 'MIXED' => 'bg-blue-100 text-blue-800', ]; @endphp {{-- ======================= Contract Summary ======================= --}}
| {{ __('Start Date') }} | {{ $employee->start_date }} |
|---|---|
| {{ __('Notification Period') }} | {{ $employee->notification_period ?? 0 }} {{ __('days') }} |
| {{ __('Contract Extended') }} | {{ $employee->contract_extension_days ?? 0 }} {{ __('days') }} |
| {{ __('Weekly off days') }} | {{ $employee->weekly_off_days ?? 0 }} {{ __('days') }} |
| {{ __('Weekly off days days') }} | @php $weeklyDaysRaw = $employee->weekly_off_days_days; $weeklyDaysArr = is_string($weeklyDaysRaw) ? json_decode($weeklyDaysRaw, true) : (is_array($weeklyDaysRaw) ? $weeklyDaysRaw : []); @endphp @if(!empty($weeklyDaysArr)) @foreach(array_reverse($weeklyDaysArr) as $day) {{ __(ucfirst($day)) }} @endforeach @endif |
| {{ __('Contract End Date') }} | {{ $contractEndDate->toDateString() }} |
| {{ __('Notification Date') }} | {{ $notificationDate ? $notificationDate->toDateString() : '-' }} |
| {{ __('Annual Entitlement (Accumulated to date)') }} | {{ number_format($totalEntitlement, 2) }} |
|---|---|
| {{ __('Used Leave Days (Annual)') }} | {{ number_format($usedDaysAllAnnual, 2) }} |
| {{ __('Remaining Leave (Annual)') }} | {{ number_format($remainingLeaveDays, 2) }} |
| {{ __('Leave Type') }} | {{ __('Conditions') }} | {{ __('Entitlement') }} | @if($freq === 'MULTIPLE_EVENT') {{ __('Available per Event') }} @else {{ __('Used') }} @endif | @if($freq === 'MULTIPLE_EVENT') {{ __('Note') }} @else {{ __('Remaining') }} @endif |
|---|---|---|---|---|
|
{{ $r['name'] }}
{{ __('Min') }}: {{ $r['min_days'] ?? '-' }} /
{{ __('Max') }}: {{ $r['max_days'] ?? '-' }}
|
{{ __('Gender') }}: {{ __($r['gender']) }} {{ __($r['pay_rule']) }} | {{ number_format($r['entitlement'], 2) }} | @if($r['frequency'] === 'MULTIPLE_EVENT') {{ number_format($r['entitlement'], 2) }} @else {{ number_format($r['used'], 2) }} @endif | @if($r['frequency'] === 'MULTIPLE_EVENT') {{ __('Per event limit') }} @else {{ number_format($r['remaining'], 2) }} @endif |
| {{ __('Totals (Annual)') }} | {{ number_format($annualTotals['entitlement'], 2) }} | {{ number_format($annualTotals['used'], 2) }} | {{ number_format($annualTotals['remaining'], 2) }} | |