From 8c574088f40b0d8d5d1b628ad7dff7e93950d149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Fri, 27 Feb 2026 22:17:15 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[payroll]=20=EA=B8=89=EC=97=AC=20?= =?UTF-8?q?=ED=99=95=EC=A0=95=20=EC=B7=A8=EC=86=8C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 확정 상태에서 작성중으로 되돌리는 기능 추가 - Model: isUnconfirmable() 상태 헬퍼 추가 - Service: unconfirmPayroll() 메서드 추가 - Controller: unconfirm() 엔드포인트 추가 - Route: POST /{id}/unconfirm 라우트 추가 - View: 확정 취소 버튼 및 JS 함수 추가 --- .../Api/Admin/HR/PayrollController.php | 35 +++++++++++++++++++ app/Models/HR/Payroll.php | 5 +++ app/Services/HR/PayrollService.php | 25 +++++++++++++ resources/views/hr/payrolls/index.blade.php | 27 ++++++++++++++ .../hr/payrolls/partials/table.blade.php | 9 +++++ routes/api.php | 1 + 6 files changed, 102 insertions(+) diff --git a/app/Http/Controllers/Api/Admin/HR/PayrollController.php b/app/Http/Controllers/Api/Admin/HR/PayrollController.php index 51aac929..123eedfc 100644 --- a/app/Http/Controllers/Api/Admin/HR/PayrollController.php +++ b/app/Http/Controllers/Api/Admin/HR/PayrollController.php @@ -278,6 +278,41 @@ public function confirm(Request $request, int $id): JsonResponse } } + /** + * 급여 확정 취소 + */ + public function unconfirm(Request $request, int $id): JsonResponse + { + if ($denied = $this->checkPayrollAccess()) { + return $denied; + } + + try { + $payroll = $this->payrollService->unconfirmPayroll($id); + + if (! $payroll) { + return response()->json([ + 'success' => false, + 'message' => '급여 확정을 취소할 수 없습니다.', + ], 404); + } + + return response()->json([ + 'success' => true, + 'message' => '급여 확정이 취소되었습니다.', + 'data' => $payroll, + ]); + } catch (\Throwable $e) { + report($e); + + return response()->json([ + 'success' => false, + 'message' => '급여 확정 취소 중 오류가 발생했습니다.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } + /** * 급여 지급 처리 */ diff --git a/app/Models/HR/Payroll.php b/app/Models/HR/Payroll.php index 59f2445f..78ca3359 100644 --- a/app/Models/HR/Payroll.php +++ b/app/Models/HR/Payroll.php @@ -141,6 +141,11 @@ public function isConfirmable(): bool return $this->status === self::STATUS_DRAFT; } + public function isUnconfirmable(): bool + { + return $this->status === self::STATUS_CONFIRMED; + } + public function isPayable(): bool { return $this->status === self::STATUS_CONFIRMED; diff --git a/app/Services/HR/PayrollService.php b/app/Services/HR/PayrollService.php index 67c04d5a..813179d0 100644 --- a/app/Services/HR/PayrollService.php +++ b/app/Services/HR/PayrollService.php @@ -278,6 +278,31 @@ public function confirmPayroll(int $id): ?Payroll return $payroll->fresh(['user']); } + /** + * 급여 확정 취소 (confirmed → draft) + */ + public function unconfirmPayroll(int $id): ?Payroll + { + $tenantId = session('selected_tenant_id', 1); + + $payroll = Payroll::query() + ->forTenant($tenantId) + ->find($id); + + if (! $payroll || ! $payroll->isUnconfirmable()) { + return null; + } + + $payroll->update([ + 'status' => Payroll::STATUS_DRAFT, + 'confirmed_at' => null, + 'confirmed_by' => null, + 'updated_by' => auth()->id(), + ]); + + return $payroll->fresh(['user']); + } + /** * 급여 지급 처리 */ diff --git a/resources/views/hr/payrolls/index.blade.php b/resources/views/hr/payrolls/index.blade.php index c71aa30c..2c5b44b9 100644 --- a/resources/views/hr/payrolls/index.blade.php +++ b/resources/views/hr/payrolls/index.blade.php @@ -888,6 +888,33 @@ function confirmPayroll(id) { }); } + // ===== 급여 확정 취소 ===== + function unconfirmPayroll(id) { + if (!confirm('이 급여의 확정을 취소하시겠습니까? 작성중 상태로 되돌아갑니다.')) return; + + fetch('{{ url("/api/admin/hr/payrolls") }}/' + id + '/unconfirm', { + method: 'POST', + headers: { + 'X-CSRF-TOKEN': '{{ csrf_token() }}', + 'Accept': 'application/json', + }, + }) + .then(r => r.json()) + .then(result => { + if (result.success) { + refreshTable(); + refreshStats(); + showToast(result.message, 'success'); + } else { + showToast(result.message, 'error'); + } + }) + .catch(err => { + console.error(err); + showToast('확정 취소 중 오류가 발생했습니다.', 'error'); + }); + } + // ===== 급여 지급 ===== function payPayroll(id) { if (!confirm('이 급여를 지급 처리하시겠습니까?')) return; diff --git a/resources/views/hr/payrolls/partials/table.blade.php b/resources/views/hr/payrolls/partials/table.blade.php index a96f3165..a75b4d92 100644 --- a/resources/views/hr/payrolls/partials/table.blade.php +++ b/resources/views/hr/payrolls/partials/table.blade.php @@ -121,6 +121,15 @@ @endif + {{-- 확정 취소 (confirmed만) --}} + @if($payroll->isUnconfirmable()) + + @endif + {{-- 지급 (confirmed만) --}} @if($payroll->isPayable())