@php $detailRows = is_array($detailRows ?? null) ? $detailRows : []; $detailOtColumns = is_array($detailOtColumns ?? null) ? $detailOtColumns : []; $detailProfil = $detailProfil ?? $profil ?? null; $dayTypeLabelResolver = static fn (string $dayType): string => match ($dayType) { 'holiday_special' => 'Holiday Special', 'holiday' => 'Holiday', 'weekend' => 'Weekend', default => 'Workday', }; $specialPaymentModeLabelResolver = static fn (?string $mode): string => match (strtolower(trim((string) $mode))) { 'payroll' => 'Gabung Payroll', 'separate' => 'Bayar Terpisah', default => '-', }; $emergencyBaselineLabelResolver = static fn (?string $mode): string => match (strtolower(trim((string) $mode))) { 'after_normal_shift' => 'Setelah jam normal selesai', 'after_normal_or_existing_ot' => 'Setelah jam normal / OT existing selesai', default => trim((string) $mode) !== '' ? trim((string) $mode) : '-', }; $emergencyMultiplierLabelResolver = static fn (?string $profile): string => match (strtolower(trim((string) $profile))) { 'default' => 'Default Emergency x2', default => trim((string) $profile) !== '' ? trim((string) $profile) : '-', }; $workTargetBonusSourceLabelResolver = static fn (?string $source): string => match (strtolower(trim((string) $source))) { 'manual_override' => 'Override manual', 'remaining_target' => 'Sisa target aktual', default => trim((string) $source) !== '' ? trim((string) $source) : '-', }; $dayTypeRowClassResolver = static fn (string $dayType): string => match ($dayType) { 'holiday_special' => 'row-holiday-special', 'holiday' => 'row-holiday', 'weekend' => 'row-weekend', default => '', }; $isHolidayLikeDayType = static fn (string $dayType): bool => in_array($dayType, ['holiday', 'holiday_special'], true); $sumPlannedHours = 0.0; $sumNormalHours = 0.0; $sumHk = 0.0; $jamTdkMasuk = 0.0; $sumOvertime = 0.0; $sumWeightedOvertime = 0.0; $otTotals = []; $detailPayrollBasis = is_array($payrollBasis ?? null) ? $payrollBasis : []; $hkHoursPerDay = (float) ($detailPayrollBasis['hours_per_day'] ?? ($detailProfil->jam_per_hari ?? 8)); $hkBaseHours = (float) ($detailPayrollBasis['monthly_hour_base'] ?? (((float) ($detailProfil->hk_basis ?? 21)) * $hkHoursPerDay)); $hkBasis = $hkHoursPerDay > 0 ? ($hkBaseHours / $hkHoursPerDay) : (float) ($detailProfil->hk_basis ?? 21); $fmt = function ($v) { return rtrim(rtrim(number_format((float) $v, 2, '.', ''), '0'), '.'); }; $resolveAttendanceDayCredit = static fn (array $detailRow, float $hkHoursPerDay, $profil): array => \App\Modules\AttendanceReview\Services\AttendanceDayCreditResolver::resolve($detailRow, $profil, $hkHoursPerDay); foreach ($detailRows as $row) { $sumPlannedHours += (float) ($row['planned_hours'] ?? 0); $sumNormalHours += (float) ($row['normal_hours'] ?? 0); $sumOvertime += (float) ($row['overtime_hours'] ?? 0); $sumWeightedOvertime += (float) ($row['weighted_overtime_hours'] ?? 0); if (!empty($row['ot_dynamic']) && is_array($row['ot_dynamic'])) { foreach ($row['ot_dynamic'] as $k => $v) { if (!isset($otTotals[$k])) { $otTotals[$k] = 0.0; } $otTotals[$k] += (float) $v; } } $dayCredit = $resolveAttendanceDayCredit((array) $row, $hkHoursPerDay, $detailProfil); $sumHk += (int) ($dayCredit['hk_flag'] ?? 0); } $totalJamHk = $hkBaseHours; $deficitRows = []; foreach ($detailRows as $row) { $dayCredit = $resolveAttendanceDayCredit((array) $row, $hkHoursPerDay, $detailProfil); if ((int) ($dayCredit['hk_flag'] ?? 0) !== 1) { continue; } $normalHours = max(0.0, (float) ($dayCredit['recognized_hours'] ?? 0)); $deficitHours = max(0.0, (float) ($dayCredit['deficit_hours'] ?? 0)); if ($deficitHours <= 0) { continue; } $deficitRows[] = [ 'work_date' => (string) ($row['work_date'] ?? '-'), 'day_name' => (string) ($row['day_name'] ?? '-'), 'day_type' => (string) ($row['day_type'] ?? 'workday'), 'leave_tag_label' => (string) ($row['leave_tag_label'] ?? ''), 'normal_hours' => $normalHours, 'deficit_hours' => $deficitHours, 'reason' => (string) ($row['reason'] ?? '-') . (!empty($dayCredit['special_bonus']) ? ' | Bonus HK ContinuesNextDay' : ''), ]; } $totalDeficitHours = collect($deficitRows)->sum('deficit_hours'); $jamTdkMasuk = max(0.0, $totalDeficitHours); $jamMasukNormal = max(0.0, $hkBaseHours - $jamTdkMasuk); $hkMasuk = $hkHoursPerDay > 0 ? $jamMasukNormal / $hkHoursPerDay : 0; $otCount = count($detailOtColumns); $remainingCols = 6 + $otCount; @endphp
Basis Jam Profil
{{ number_format($hkBaseHours, 2, ',', '.') }} jam
HK basis: {{ number_format($hkBasis, 2, ',', '.') }} x {{ number_format($hkHoursPerDay, 2, ',', '.') }} jam
Total Jam Kurang
{{ number_format($jamTdkMasuk, 2, ',', '.') }} jam
Pengurang dari basis jam profil
HK Masuk Riil
{{ number_format($hkMasuk, 2, ',', '.') }}/{{ number_format($hkBasis, 2, ',', '.') }}
Jam normal: {{ number_format($jamMasukNormal, 2, ',', '.') }} / {{ number_format($hkBaseHours, 2, ',', '.') }}
@if (!empty($deficitRows))
Audit Kekurangan Jam
Tanggal yang mengurangi basis jam profil.
{{ count($deficitRows) }} hari bermasalah
@foreach ($deficitRows as $auditRow) @php $auditRowClass = $auditRow['deficit_hours'] >= $hkHoursPerDay ? 'audit-row-heavy' : 'audit-row-light'; @endphp @endforeach
Tanggal Jenis Jam Diakui Kurang Reason
{{ $auditRow['work_date'] }} ({{ $auditRow['day_name'] }}) {{ ucfirst($auditRow['day_type']) }} @if ($auditRow['leave_tag_label'] !== '')
{{ $auditRow['leave_tag_label'] }} @endif
{{ number_format($auditRow['normal_hours'], 2, ',', '.') }} jam {{ number_format($auditRow['deficit_hours'], 2, ',', '.') }} jam {!! nl2br(e($auditRow['reason'])) !!}
Total Jam Kurang {{ number_format($totalDeficitHours, 2, ',', '.') }} jam
@endif
@foreach ($detailOtColumns as $otCol) @php $rawLabel = (string) ($otCol['label'] ?? ''); $multiplier = (float) ($otCol['multiplier'] ?? 0); $percentLabel = $multiplier > 0 ? rtrim(rtrim(number_format($multiplier * 100, 1, '.', ''), '0'), '.') . '%' : ''; $headerLabel = $rawLabel !== '' ? $rawLabel : ($percentLabel !== '' ? $percentLabel : 'OT'); @endphp @endforeach @forelse ($detailRows as $idx => $row) @php $dayType = (string) ($row['day_type'] ?? 'workday'); $dayClass = $dayTypeRowClassResolver($dayType); $dayLabel = $dayTypeLabelResolver($dayType); @endphp @foreach ($detailOtColumns as $otCol) @endforeach @empty @endforelse @if (!empty($detailRows)) @foreach ($detailOtColumns as $otCol) @php $k = $otCol['key']; @endphp @endforeach @endif
No. Tanggal In Out Jam Normal (Plan) HK Jam Aktual / Efektif Jam Lembur Bersih{{ $headerLabel }}Jam Lembur xKali In Lembur Out Lembur Status Reason
{{ $idx + 1 }} {{ $row['work_date'] }} ({{ $row['day_name'] }}) @if (!empty($row['day_type_description']) && $row['day_type_description'] !== '-') - {{ $row['day_type_description'] }} @endif
{{ $dayLabel }} @if (!empty($row['leave_tag_label'])) {{ $row['leave_tag_label'] }} @endif @if (!empty($row['special_overtime_label'])) {{ $row['special_overtime_label'] }} @endif @if (!empty($row['special_overtime_mode'])) {{ $row['special_overtime_mode'] }} @endif
@if (!empty($row['applied_shift_code']))
Shift {{ $row['applied_shift_code'] }}
@endif @if (!empty($row['has_face_image']))
@endif
{{ $row['in_time'] }} @php $inEff = (string) ($row['in_time'] ?? '-'); $inShadow = (string) ($row['in_time_shadow'] ?? '-'); $inRaw = (string) ($row['in_time_real'] ?? '-'); $inReal = '-'; if ($inShadow !== '-' && $inShadow !== '') { $inReal = $inShadow; } elseif ($inRaw !== '-' && $inRaw !== '') { $inReal = $inRaw; } @endphp @if ($inReal !== '-')
{{ $inReal }} (actual) @endif
@php $specialCodeView = strtolower((string) ($row['special_overtime_code'] ?? '')); $specialModeView = strtolower((string) ($row['special_overtime_mode'] ?? '')); $specialLabelView = strtolower((string) ($row['special_overtime_label'] ?? '')); $displayOutTime = (string) ($row['out_time'] ?? '-'); if (($specialCodeView === 'continues_work_day' || str_contains($specialModeView, 'continues') || str_contains($specialLabelView, 'special overtime')) && str_contains($specialModeView, 'carry')) { $displayOutTime = '-'; } @endphp {{ $displayOutTime }} @php $outEff = (string) ($row['out_time'] ?? '-'); $outShadow = (string) ($row['out_time_shadow'] ?? '-'); $outRaw = (string) ($row['out_time_real'] ?? '-'); $outReal = '-'; if ($outShadow !== '-' && $outShadow !== '') { $outReal = $outShadow; } elseif ($outRaw !== '-' && $outRaw !== '') { $outReal = $outRaw; } @endphp @if ($outReal !== '-' && $outReal !== $outEff)
{{ $outReal }} (actual) @endif
{{ number_format((float) $row['planned_hours'], 2, '.', '') }} @if (!empty($row['planned_in_time']) && !empty($row['planned_out_time']))
{{ $row['planned_in_time'] }} - {{ $row['planned_out_time'] }} @endif
@php $dayType = (string) ($row['day_type'] ?? 'workday'); $dayCredit = $resolveAttendanceDayCredit((array) $row, $hkHoursPerDay, $detailProfil); $hkFlag = (int) ($dayCredit['hk_flag'] ?? 0); $specialCodeHk = strtolower((string) ($row['special_overtime_code'] ?? '')); $specialModeHk = strtolower((string) ($row['special_overtime_mode'] ?? '')); $specialLabelHk = strtolower((string) ($row['special_overtime_label'] ?? '')); $isContinuesVisual = $specialCodeHk === 'continues_work_day' || str_contains($specialModeHk, 'continues') || str_contains($specialLabelHk, 'special overtime'); @endphp {{ $hkFlag }} @if ($hkFlag === 1) ({{ $fmt($hkHoursPerDay) }} jam) @php $isAlphaWorkday = ($dayType === 'workday' && $hkFlag === 1 && (float) ($row['actual_hours'] ?? 0) <= 0 && empty($row['leave_tag_label']) && empty($dayCredit['special_bonus'])); @endphp @if ($isAlphaWorkday && !$isContinuesVisual)
HK 1 (Alpha)
@elseif (!empty($dayCredit['holiday_paid']))
Holiday dibayar: +{{ $fmt($dayCredit['recognized_hours'] ?? $hkHoursPerDay) }} jam
@if (!empty($dayCredit['pending_spl']))
Log hadir menunggu SPL; belum dihitung lembur
@endif @elseif (!empty($dayCredit['special_bonus']))
HK Bonus ContinuesNextDay
@endif @endif
@php $dayTypeValue = (string) ($row['day_type'] ?? 'workday'); $actualInTime = trim((string) ($row['in_time'] ?? '-')); $actualOutTime = trim((string) ($row['out_time'] ?? '-')); $hasActualInOut = $actualInTime !== '' && $actualInTime !== '-' && $actualOutTime !== '' && $actualOutTime !== '-'; $isHolidayWeekend = in_array($dayTypeValue, ['holiday', 'holiday_special', 'weekend'], true); $sourceHours = $isHolidayWeekend && (float) ($row['overtime_hours_gross'] ?? 0) > 0 ? (float) ($row['overtime_hours_gross'] ?? 0) : (float) ($row['actual_hours'] ?? 0); $sourceLabel = $isHolidayWeekend ? 'Total Jam Lembur' : 'Jam Aktual'; $hasNormalBreakCut = (float) ($row['break_hours'] ?? 0) > 0 && $sourceHours > 0 && $hasActualInOut; $actualDisplayHours = $hasNormalBreakCut ? max(0, $sourceHours - (float) ($row['break_hours'] ?? 0)) : $sourceHours; $specialPayload = is_array($row['special_overtime_payload'] ?? null) ? $row['special_overtime_payload'] : []; $specialCodeHours = strtolower((string) ($row['special_overtime_code'] ?? '')); $specialModeHours = strtolower((string) ($row['special_overtime_mode'] ?? '')); $isContinuesHours = $specialCodeHours === 'continues_work_day' || str_contains($specialModeHours, 'continues'); $continuesTargetCoverageHours = round((float) ($specialPayload['continues_target_coverage_hours'] ?? 0), 2); $continuesNextDayWorkHours = round((float) ($specialPayload['continues_next_day_work_hours'] ?? 0), 2); $continuesShortageHours = round((float) ($specialPayload['continues_effective_shortage_hours'] ?? ($specialPayload['continues_shortage_hours'] ?? 0)), 2); $continuesOriginalShortageHours = round((float) ($specialPayload['continues_original_shortage_hours'] ?? $continuesShortageHours), 2); $continuesEffectiveHours = round(max(0.0, $continuesNextDayWorkHours + $continuesShortageHours), 2); $continuesConversionMode = strtolower(trim((string) ($specialPayload['continues_conversion_mode'] ?? ''))); $continuesConversionLabel = $continuesConversionMode === 'bonus_hk' ? 'Bonus 1 HK' : ($continuesConversionMode === 'bonus_hours' ? 'Bonus Jam' : 'Bonus Continues'); $continuesCarryActualOut = trim((string) ($specialPayload['continues_carry_actual_out'] ?? '')); $continuesBonusCutoff = trim((string) ($specialPayload['continues_bonus_cutoff_time'] ?? '16:00')); $continuesBonusAwarded = (bool) ($specialPayload['continues_bonus_awarded'] ?? ($continuesShortageHours > 0)); $isEmergencyHours = $specialCodeHours === 'emergency_on_call'; $emergencyBonusHours = round((float) ($specialPayload['emergency_bonus_hours'] ?? 0), 2); $emergencyBonusNominal = round((float) ($specialPayload['emergency_bonus_nominal'] ?? 0), 2); $emergencyBaselineLabel = $emergencyBaselineLabelResolver($specialPayload['emergency_baseline_mode'] ?? ''); $emergencyMultiplierLabel = $emergencyMultiplierLabelResolver($specialPayload['emergency_multiplier_profile'] ?? ''); $specialPaymentModeLabel = $specialPaymentModeLabelResolver($specialPayload['bonus_payment_mode'] ?? ''); $isTargetWorkHours = $specialCodeHours === 'work_target'; $workTargetBonusHours = round((float) ($specialPayload['work_target_bonus_hours'] ?? 0), 2); $workTargetBonusCapHours = round((float) ($specialPayload['work_target_bonus_cap_hours'] ?? 0), 2); $workTargetMinimumActualHours = round((float) ($specialPayload['work_target_minimum_actual_hours'] ?? 0), 2); $workTargetCompletionRequired = (bool) ($specialPayload['work_target_completion_required'] ?? false); $workTargetBonusSourceLabel = $workTargetBonusSourceLabelResolver($specialPayload['work_target_bonus_source'] ?? ''); @endphp @if ($isContinuesHours && str_contains($specialModeHours, 'carry')) {{ number_format($continuesEffectiveHours, 2, '.', '') }}
Jam Actual Carry: {{ number_format($continuesNextDayWorkHours, 2, '.', '') }} jam @if ($continuesCarryActualOut !== '')
Checkout Carry Actual: {{ $continuesCarryActualOut }} @endif
{{ $continuesConversionLabel }} Carry: {{ number_format($continuesShortageHours, 2, '.', '') }} jam @if (!$continuesBonusAwarded && $continuesOriginalShortageHours > 0)
Bonus carry tidak diberikan karena checkout actual <= {{ $continuesBonusCutoff }} @endif
Perkalian Carry: {{ number_format($continuesNextDayWorkHours, 2, '.', '') }} actual + {{ number_format($continuesShortageHours, 2, '.', '') }} bonus = {{ number_format($continuesEffectiveHours, 2, '.', '') }} jam
Hasil Jam Efektif Carry: {{ number_format($continuesEffectiveHours, 2, '.', '') }} jam @elseif ($isContinuesHours) {{ number_format((float) ($row['actual_hours'] ?? $actualDisplayHours), 2, '.', '') }}
Jam Efektif Anchor: {{ number_format((float) ($row['actual_hours'] ?? $actualDisplayHours), 2, '.', '') }} jam @if ($continuesTargetCoverageHours > 0)
Target Coverage Continues: {{ number_format($continuesTargetCoverageHours, 2, '.', '') }} jam @endif @if (!empty($row['out_time']) && $row['out_time'] !== '-')
Bridge approved hingga {{ $row['out_time'] }} @endif @elseif ($isEmergencyHours) {{ number_format($actualDisplayHours, 2, '.', '') }}
Jam Aktual Emergency: {{ number_format($actualDisplayHours, 2, '.', '') }} jam
Baseline Emergency: {{ $emergencyBaselineLabel }}
Profile Multiplier: {{ $emergencyMultiplierLabel }} @if ($emergencyBonusHours > 0)
Tambahan Jam Emergency: {{ number_format($emergencyBonusHours, 2, '.', '') }} jam @endif @if ($emergencyBonusNominal > 0)
Insentif Emergency: Rp {{ number_format($emergencyBonusNominal, 2, ',', '.') }} @endif @if ($specialPaymentModeLabel !== '-')
Pembayaran Bonus: {{ $specialPaymentModeLabel }} @endif
Actual hour tetap jujur, emergency dimulai setelah baseline selesai. @elseif ($isTargetWorkHours) {{ number_format($actualDisplayHours, 2, '.', '') }}
Jam Aktual Target Work: {{ number_format($actualDisplayHours, 2, '.', '') }} jam
Sumber Bonus: {{ $workTargetBonusSourceLabel }} @if ($workTargetMinimumActualHours > 0)
Minimum Aktual Bonus: {{ number_format($workTargetMinimumActualHours, 2, '.', '') }} jam @endif @if ($workTargetBonusCapHours > 0)
Cap Bonus Jam: {{ number_format($workTargetBonusCapHours, 2, '.', '') }} jam @endif @if ($workTargetBonusHours > 0)
Override Bonus Manual: {{ number_format($workTargetBonusHours, 2, '.', '') }} jam @endif @if ($workTargetCompletionRequired)
Bonus hanya berlaku jika pekerjaan dinyatakan selesai. @endif @if ($specialPaymentModeLabel !== '-')
Pembayaran Bonus: {{ $specialPaymentModeLabel }} @endif
Jam actual tetap jujur, bonus target tidak menambah jam hadir aktual. @elseif ($hasNormalBreakCut) {{ number_format($actualDisplayHours, 2, '.', '') }}
{{ $sourceLabel }}: {{ number_format($sourceHours, 2, '.', '') }} jam
Istirahat Normal: {{ number_format((float) ($row['break_hours'] ?? 0), 2, '.', '') }} jam
Efektif: {{ number_format($actualDisplayHours, 2, '.', '') }} jam @else {{ number_format($actualDisplayHours, 2, '.', '') }} @endif
{{ number_format((float) $row['overtime_hours'], 2, '.', '') }} @if ((float) ($row['overtime_break_hours'] ?? 0) > 0)
Gross {{ number_format((float) ($row['overtime_hours_gross'] ?? 0), 2, '.', '') }} - Break {{ number_format((float) ($row['overtime_break_hours'] ?? 0), 2, '.', '') }} @endif @if (!empty($row['overtime_detail_segments']))
@foreach ($row['overtime_detail_segments'] as $segment)
{{ $segment['label'] }} = {{ number_format((float) ($segment['hours'] ?? 0), 2, '.', '') }}
@endforeach
@endif
{{ number_format((float) ($row['ot_dynamic'][$otCol['key']] ?? 0), 2, '.', '') }}{{ number_format((float) $row['weighted_overtime_hours'], 2, '.', '') }} {{ $row['overtime_in_time'] }} {{ $row['overtime_out_time'] }} @if (!empty($row['leave_tag_label'])) Auto Exempt (Leave) @elseif (!empty($row['special_overtime_label'])) {{ $row['special_overtime_label'] }} @if (!empty($row['special_overtime_mode']))
{{ $row['special_overtime_mode'] }}
@endif @elseif ($row['is_approved']) Approved @else Draft @endif
{!! nl2br(e($row['reason'])) !!}
Belum ada data.
Total Jam Normal (Plan) {{ number_format($sumPlannedHours, 2, '.', '') }} - -
Total Jam Kurang - {{ number_format($jamTdkMasuk, 2, '.', '') }}
Jam Masuk Normal ({{ $fmt($totalJamHk) }} - Total Jam Kurang) - {{ number_format($jamMasukNormal, 2, '.', '') }}
HK Masuk (hari) - {{ number_format($hkMasuk, 2, '.', '') }} hari
Total Lembur (OT) {{ $fmt($sumOvertime) }}{{ $fmt($otTotals[$k] ?? 0) }}{{ $fmt($sumWeightedOvertime) }}
@once @endonce