Files
sam-manage/resources/views/hr/payrolls/partials/table.blade.php
김보곤 bcb45c9362 feat: [payroll] 급여 확정 취소 기능 추가
- 확정 상태에서 작성중으로 되돌리는 기능 추가
- Model: isUnconfirmable() 상태 헬퍼 추가
- Service: unconfirmPayroll() 메서드 추가
- Controller: unconfirm() 엔드포인트 추가
- Route: POST /{id}/unconfirm 라우트 추가
- View: 확정 취소 버튼 및 JS 함수 추가
2026-02-27 22:17:34 +09:00

197 lines
12 KiB
PHP

{{-- 급여 목록 테이블 (HTMX로 로드) --}}
@php
use App\Models\HR\Payroll;
@endphp
<x-table-swipe>
<table class="min-w-full">
<thead class="bg-gray-50 border-b border-gray-200">
<tr>
<th class="px-6 py-3 text-left text-sm font-semibold text-gray-600">사원</th>
<th class="px-6 py-3 text-left text-sm font-semibold text-gray-600">부서</th>
<th class="px-6 py-3 text-right text-sm font-semibold text-gray-600">기본급</th>
<th class="px-6 py-3 text-right text-sm font-semibold text-gray-600">수당</th>
<th class="px-6 py-3 text-right text-sm font-semibold text-gray-600">총지급액</th>
<th class="px-6 py-3 text-right text-sm font-semibold text-gray-600">공제액</th>
<th class="px-6 py-3 text-right text-sm font-semibold text-gray-600">실수령액</th>
<th class="px-6 py-3 text-center text-sm font-semibold text-gray-600">상태</th>
<th class="px-4 py-3 text-center text-sm font-semibold text-gray-600">작업</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-100">
@forelse($payrolls as $payroll)
@php
$profile = $payroll->user?->tenantProfiles?->first();
$department = $profile?->department;
$displayName = $profile?->display_name ?? $payroll->user?->name ?? '-';
$color = Payroll::STATUS_COLORS[$payroll->status] ?? 'gray';
$label = Payroll::STATUS_MAP[$payroll->status] ?? $payroll->status;
$allowancesTotal = 0;
if ($payroll->allowances) {
foreach ($payroll->allowances as $a) { $allowancesTotal += ($a['amount'] ?? 0); }
}
$overtimeBonus = ($payroll->overtime_pay ?? 0) + ($payroll->bonus ?? 0) + $allowancesTotal;
@endphp
<tr class="hover:bg-gray-50 transition-colors">
{{-- 사원 --}}
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center gap-2">
<div class="shrink-0 w-8 h-8 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-xs font-medium">
{{ mb_substr($displayName, 0, 1) }}
</div>
<span class="text-sm font-medium text-gray-900">{{ $displayName }}</span>
</div>
</td>
{{-- 부서 --}}
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-700">
{{ $department?->name ?? '-' }}
</td>
{{-- 기본급 --}}
<td class="px-6 py-4 whitespace-nowrap text-right text-sm text-gray-700">
{{ number_format($payroll->base_salary) }}
</td>
{{-- 수당 (고정연장근로+상여+기타) --}}
<td class="px-6 py-4 whitespace-nowrap text-right text-sm text-gray-500">
{{ $overtimeBonus > 0 ? number_format($overtimeBonus) : '-' }}
</td>
{{-- 총지급액 --}}
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium text-gray-800">
{{ number_format($payroll->gross_salary) }}
</td>
{{-- 공제액 --}}
<td class="px-6 py-4 whitespace-nowrap text-right text-sm text-red-600">
{{ number_format($payroll->total_deductions) }}
</td>
{{-- 실수령액 --}}
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium text-emerald-600">
{{ number_format($payroll->net_salary) }}
</td>
{{-- 상태 --}}
<td class="px-6 py-4 whitespace-nowrap text-center">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-{{ $color }}-100 text-{{ $color }}-700">
{{ $label }}
</span>
</td>
{{-- 작업 --}}
<td class="px-4 py-4 whitespace-nowrap text-center">
<div class="flex items-center justify-center gap-1">
{{-- 수정 (draft만) --}}
@if($payroll->isEditable())
<button type="button" onclick="openEditPayrollModal({{ $payroll->id }}, {{ json_encode([
'user_id' => $payroll->user_id,
'user_name' => $displayName,
'base_salary' => $payroll->base_salary,
'overtime_pay' => $payroll->overtime_pay,
'bonus' => $payroll->bonus,
'allowances' => $payroll->allowances,
'pension' => $payroll->pension,
'health_insurance' => $payroll->health_insurance,
'long_term_care' => $payroll->long_term_care,
'employment_insurance' => $payroll->employment_insurance,
'income_tax' => $payroll->income_tax,
'resident_tax' => $payroll->resident_tax,
'deductions' => $payroll->deductions,
'note' => $payroll->note,
]) }})" class="p-1 text-blue-600 hover:text-blue-800" title="수정">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
</button>
<button type="button" onclick="deletePayroll({{ $payroll->id }})" class="p-1 text-red-600 hover:text-red-800" title="삭제">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
</svg>
</button>
@endif
{{-- 확정 (draft만) --}}
@if($payroll->isConfirmable())
<button type="button" onclick="confirmPayroll({{ $payroll->id }})" class="p-1 text-blue-600 hover:text-blue-800" title="확정">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</button>
@endif
{{-- 확정 취소 (confirmed만) --}}
@if($payroll->isUnconfirmable())
<button type="button" onclick="unconfirmPayroll({{ $payroll->id }})" class="p-1 text-orange-600 hover:text-orange-800" title="확정 취소">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"/>
</svg>
</button>
@endif
{{-- 지급 (confirmed만) --}}
@if($payroll->isPayable())
<button type="button" onclick="payPayroll({{ $payroll->id }})" class="p-1 text-emerald-600 hover:text-emerald-800" title="지급처리">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z"/>
</svg>
</button>
@endif
{{-- 상세보기 (paid) --}}
@if($payroll->status === 'paid')
<button type="button" onclick="openPayrollDetail({{ $payroll->id }}, {{ json_encode([
'user_name' => $displayName,
'department' => $department?->name ?? '-',
'period' => $payroll->period_label,
'base_salary' => $payroll->base_salary,
'overtime_pay' => $payroll->overtime_pay,
'bonus' => $payroll->bonus,
'allowances' => $payroll->allowances,
'gross_salary' => $payroll->gross_salary,
'income_tax' => $payroll->income_tax,
'resident_tax' => $payroll->resident_tax,
'health_insurance' => $payroll->health_insurance,
'long_term_care' => $payroll->long_term_care,
'pension' => $payroll->pension,
'employment_insurance' => $payroll->employment_insurance,
'deductions' => $payroll->deductions,
'total_deductions' => $payroll->total_deductions,
'net_salary' => $payroll->net_salary,
'confirmed_at' => $payroll->confirmed_at?->format('Y-m-d H:i'),
'paid_at' => $payroll->paid_at?->format('Y-m-d H:i'),
'note' => $payroll->note,
]) }})" class="p-1 text-gray-600 hover:text-gray-800" title="상세보기">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
</svg>
</button>
@endif
</div>
</td>
</tr>
@empty
<tr>
<td colspan="9" class="px-6 py-12 text-center">
<div class="flex flex-col items-center gap-2">
<svg class="w-12 h-12 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z"/>
</svg>
<p class="text-gray-500">급여 기록이 없습니다.</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</x-table-swipe>
{{-- 페이지네이션 --}}
@if($payrolls->hasPages())
<div class="px-6 py-4 border-t border-gray-200 bg-gray-50">
{{ $payrolls->links() }}
</div>
@endif