From b905c0a2c6d65b2493e10991179cf5e457c9d21d Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Thu, 13 Aug 2026 17:36:55 +0700 Subject: [PATCH] feat: update EmployeeAdvance status values and related logic, modify notifications accordingly --- .ai/REFERENCE.md | 2 +- app/Enums/EmployeeAdvanceStatus.php | 12 +++++----- app/Models/EmployeeAdvance.php | 12 +++++----- .../Admin/Finance/EmployeeAdvanceService.php | 24 +++++++++---------- app/Services/AnalysisService.php | 5 ++-- app/Services/DashboardService.php | 2 +- database/factories/EmployeeAdvanceFactory.php | 2 +- .../finance/employee-advance/columns.tsx | 22 ++++++++--------- tests/Feature/Admin/NotificationTest.php | 4 ++-- 9 files changed, 43 insertions(+), 42 deletions(-) diff --git a/.ai/REFERENCE.md b/.ai/REFERENCE.md index 5b3bcd6..07521c5 100644 --- a/.ai/REFERENCE.md +++ b/.ai/REFERENCE.md @@ -288,7 +288,7 @@ ## Enums |------|--------|---------| | `CashTransactionType` | deposit, expense, transfer, withdrawal | cash_transactions.type | | `CuttingStatus` | in_progress, completed, cancelled | cuttings.status | -| `EmployeeAdvanceStatus` | pending, approved, rejected, paid, cancelled | employee_advances.status | +| `EmployeeAdvanceStatus` | pending, disbursed, partial, repaid, rejected | employee_advances.status | | `EmploymentStatus` | full_time, part_time, contract, internship, resigned | employees.employment_status | | `Gender` | male, female | user_profiles.gender | | `LeaveRequestStatus` | pending, approved, rejected, cancelled | leave_requests.status | diff --git a/app/Enums/EmployeeAdvanceStatus.php b/app/Enums/EmployeeAdvanceStatus.php index 7dc5ccd..c5ad3de 100644 --- a/app/Enums/EmployeeAdvanceStatus.php +++ b/app/Enums/EmployeeAdvanceStatus.php @@ -8,20 +8,20 @@ enum EmployeeAdvanceStatus: string { use HasValues; - case APPROVED = 'approved'; - case CANCELLED = 'cancelled'; - case PAID = 'paid'; + case DISBURSED = 'disbursed'; + case PARTIAL = 'partial'; case PENDING = 'pending'; case REJECTED = 'rejected'; + case REPAID = 'repaid'; public function label(): string { return match ($this) { - self::APPROVED => 'Disetujui', - self::CANCELLED => 'Dibatalkan', - self::PAID => 'Dibayar', + self::DISBURSED => 'Dikeluarkan', + self::PARTIAL => 'Dicicil', self::PENDING => 'Menunggu', self::REJECTED => 'Ditolak', + self::REPAID => 'Lunas', }; } } diff --git a/app/Models/EmployeeAdvance.php b/app/Models/EmployeeAdvance.php index 09b059e..d1ce7f3 100644 --- a/app/Models/EmployeeAdvance.php +++ b/app/Models/EmployeeAdvance.php @@ -75,21 +75,21 @@ protected function statusLabel(): Attribute } #[Scope] - protected function approved(Builder $query): void + protected function disbursed(Builder $query): void { - $query->where('status', EmployeeAdvanceStatus::APPROVED); + $query->where('status', EmployeeAdvanceStatus::DISBURSED); } #[Scope] - protected function cancelled(Builder $query): void + protected function partial(Builder $query): void { - $query->where('status', EmployeeAdvanceStatus::CANCELLED); + $query->where('status', EmployeeAdvanceStatus::PARTIAL); } #[Scope] - protected function paid(Builder $query): void + protected function repaid(Builder $query): void { - $query->where('status', EmployeeAdvanceStatus::PAID); + $query->where('status', EmployeeAdvanceStatus::REPAID); } #[Scope] diff --git a/app/Services/Admin/Finance/EmployeeAdvanceService.php b/app/Services/Admin/Finance/EmployeeAdvanceService.php index e1e4691..108ae99 100644 --- a/app/Services/Admin/Finance/EmployeeAdvanceService.php +++ b/app/Services/Admin/Finance/EmployeeAdvanceService.php @@ -63,13 +63,13 @@ public function store(array $data): EmployeeAdvance public function update(EmployeeAdvance $employeeAdvance, array $data): EmployeeAdvance { - if ($employeeAdvance->status === EmployeeAdvanceStatus::PAID) { + if ($employeeAdvance->status === EmployeeAdvanceStatus::REPAID) { throw ValidationException::withMessages([ - 'amount' => 'Kasbon yang sudah dibayar tidak dapat diedit.', + 'amount' => 'Kasbon yang sudah dikembalikan tidak dapat diedit.', ]); } - if ($employeeAdvance->status === EmployeeAdvanceStatus::APPROVED && $employeeAdvance->cash_transaction_id) { + if ($employeeAdvance->status === EmployeeAdvanceStatus::DISBURSED && $employeeAdvance->cash_transaction_id) { $oldAmount = $employeeAdvance->amount; $newAmount = $data['amount']; @@ -113,7 +113,7 @@ public function update(EmployeeAdvance $employeeAdvance, array $data): EmployeeA public function destroy(EmployeeAdvance $employeeAdvance): bool { return DB::transaction(function () use ($employeeAdvance) { - if ($employeeAdvance->status === EmployeeAdvanceStatus::APPROVED && $employeeAdvance->cash_transaction_id) { + if ($employeeAdvance->status === EmployeeAdvanceStatus::DISBURSED && $employeeAdvance->cash_transaction_id) { $this->creditCash( $employeeAdvance->amount, 'Pembatalan kasbon: '.$employeeAdvance->description, @@ -122,7 +122,7 @@ public function destroy(EmployeeAdvance $employeeAdvance): bool $employeeAdvance->cashTransaction()->delete(); } - if ($employeeAdvance->status === EmployeeAdvanceStatus::PAID) { + if ($employeeAdvance->status === EmployeeAdvanceStatus::REPAID || $employeeAdvance->status === EmployeeAdvanceStatus::PARTIAL) { $this->creditCash( $employeeAdvance->amount, 'Pembatalan kasbon: '.$employeeAdvance->description, @@ -159,7 +159,7 @@ public function approve(EmployeeAdvance $employeeAdvance): EmployeeAdvance $employeeAdvance->update([ 'cash_transaction_id' => $cashTransaction->id, - 'status' => EmployeeAdvanceStatus::APPROVED, + 'status' => EmployeeAdvanceStatus::DISBURSED, 'verified_by_id' => auth()->id(), 'verified_at' => now(), ]); @@ -169,8 +169,8 @@ public function approve(EmployeeAdvance $employeeAdvance): EmployeeAdvance NotificationService::notify( roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO], - title: 'Kasbon Disetujui', - body: 'Kasbon sebesar Rp '.number_format($employeeAdvance->amount, 0, ',', '.').' telah disetujui'.' oleh '.auth()->user()->full_name.'.', + title: 'Kasbon Dikeluarkan', + body: 'Kasbon sebesar Rp '.number_format($employeeAdvance->amount, 0, ',', '.').' telah disetujui dan dikeluarkan'.' oleh '.auth()->user()->full_name.'.', url: route('admin.finance.employee-advances.index'), additionalUser: $employeeAdvance->employee->user ?? null, ); @@ -208,7 +208,7 @@ public function pay(EmployeeAdvance $employeeAdvance, int $amount): EmployeeAdva $employeeAdvance->update([ 'paid_amount' => $newPaidAmount, - 'status' => $isFullyPaid ? EmployeeAdvanceStatus::PAID : $employeeAdvance->status, + 'status' => $isFullyPaid ? EmployeeAdvanceStatus::REPAID : EmployeeAdvanceStatus::PARTIAL, 'paid_by_id' => $isFullyPaid ? auth()->id() : $employeeAdvance->paid_by_id, 'paid_at' => $isFullyPaid ? now() : $employeeAdvance->paid_at, ]); @@ -216,13 +216,13 @@ public function pay(EmployeeAdvance $employeeAdvance, int $amount): EmployeeAdva return $employeeAdvance; }); - $notificationBody = $employeeAdvance->status === EmployeeAdvanceStatus::PAID - ? 'Kasbon sebesar Rp '.number_format($employeeAdvance->amount, 0, ',', '.').' telah dibayar lunas'.' oleh '.auth()->user()->full_name.'.' + $notificationBody = $employeeAdvance->status === EmployeeAdvanceStatus::REPAID + ? 'Kasbon sebesar Rp '.number_format($employeeAdvance->amount, 0, ',', '.').' telah dikembalikan lunas'.' oleh '.auth()->user()->full_name.'.' : 'Pembayaran kasbon sebesar Rp '.number_format($amount, 0, ',', '.').' oleh '.auth()->user()->full_name.'. Sisa: Rp '.number_format($employeeAdvance->amount - $employeeAdvance->paid_amount, 0, ',', '.').'.'; NotificationService::notify( roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO], - title: $employeeAdvance->status === EmployeeAdvanceStatus::PAID ? 'Kasbon Dibayar' : 'Pembayaran Kasbon', + title: $employeeAdvance->status === EmployeeAdvanceStatus::REPAID ? 'Kasbon Dikembalikan' : 'Pembayaran Kasbon', body: $notificationBody, url: route('admin.finance.employee-advances.index'), additionalUser: $employeeAdvance->employee->user ?? null, diff --git a/app/Services/AnalysisService.php b/app/Services/AnalysisService.php index f71e7d9..92bf4af 100644 --- a/app/Services/AnalysisService.php +++ b/app/Services/AnalysisService.php @@ -3,6 +3,7 @@ namespace App\Services; use App\Enums\CashTransactionType; +use App\Enums\EmployeeAdvanceStatus; use App\Enums\OrderChannel; use App\Enums\OrderStatus; use App\Enums\PaymentType; @@ -286,7 +287,7 @@ public function getExpenseSummary(?string $startDate, ?string $endDate): array $expenseQuery = Expense::query(); $this->applyDateFilter($expenseQuery, $startDate, $endDate, 'expenses.created_at'); - $advanceQuery = EmployeeAdvance::where('status', 'paid'); + $advanceQuery = EmployeeAdvance::where('status', EmployeeAdvanceStatus::REPAID); $this->applyDateFilter($advanceQuery, $startDate, $endDate, 'employee_advances.created_at'); $purchaseQuery = Purchase::query(); @@ -326,7 +327,7 @@ public function getMonthlyExpense(?string $startDate, ?string $endDate): array ->get() ->keyBy('month'); - $advanceMonthly = EmployeeAdvance::where('status', 'paid'); + $advanceMonthly = EmployeeAdvance::where('status', EmployeeAdvanceStatus::REPAID); $this->applyDateFilter($advanceMonthly, $startDate, $endDate, 'employee_advances.created_at'); $advanceByMonth = (clone $advanceMonthly)->toBase() diff --git a/app/Services/DashboardService.php b/app/Services/DashboardService.php index 709f12c..380574e 100644 --- a/app/Services/DashboardService.php +++ b/app/Services/DashboardService.php @@ -113,7 +113,7 @@ public function getExpenseSummary(): array ->first(); $advanceTotal = EmployeeAdvance::whereDate('created_at', $today) - ->approved() + ->disbursed() ->selectRaw('COALESCE(SUM(amount), 0) as total') ->first(); diff --git a/database/factories/EmployeeAdvanceFactory.php b/database/factories/EmployeeAdvanceFactory.php index be4fec8..e25a9b9 100644 --- a/database/factories/EmployeeAdvanceFactory.php +++ b/database/factories/EmployeeAdvanceFactory.php @@ -17,7 +17,7 @@ public function definition(): array 'paid_amount' => 0, 'description' => fake()->sentence(), 'due_date' => fake()->date(), - 'status' => fake()->randomElement(['pending', 'approved', 'paid', 'rejected', 'cancelled']), + 'status' => fake()->randomElement(['pending', 'disbursed', 'partial', 'repaid', 'rejected']), ]; } } diff --git a/resources/js/pages/admin/finance/employee-advance/columns.tsx b/resources/js/pages/admin/finance/employee-advance/columns.tsx index 3b13b72..3cef54f 100644 --- a/resources/js/pages/admin/finance/employee-advance/columns.tsx +++ b/resources/js/pages/admin/finance/employee-advance/columns.tsx @@ -28,7 +28,7 @@ export type EmployeeAdvance = { description: string; due_date: string; formatted_due_date: string; - status: 'pending' | 'approved' | 'paid' | 'rejected' | 'cancelled'; + status: 'pending' | 'disbursed' | 'partial' | 'repaid' | 'rejected'; created_at: string; formatted_created_at: string; employee: { @@ -48,22 +48,22 @@ function getStatusBadge(status: string) { label: 'Menunggu', className: 'bg-yellow-100 text-yellow-800 hover:bg-yellow-100', }, - approved: { - label: 'Disetujui', + disbursed: { + label: 'Dikeluarkan', className: 'bg-green-100 text-green-800 hover:bg-green-100', }, - paid: { - label: 'Dibayar', + partial: { + label: 'Cicilan', + className: 'bg-orange-100 text-orange-800 hover:bg-orange-100', + }, + repaid: { + label: 'Lunas', className: 'bg-blue-100 text-blue-800 hover:bg-blue-100', }, rejected: { label: 'Ditolak', className: 'bg-red-100 text-red-800 hover:bg-red-100', }, - cancelled: { - label: 'Dibatalkan', - className: 'bg-gray-100 text-gray-800 hover:bg-gray-100', - }, }; const config = statusConfig[status] ?? statusConfig.pending; @@ -127,7 +127,7 @@ export function createEmployeeAdvanceColumns( cell: ({ row }) => { const employeeAdvance = row.original; - if (employeeAdvance.status === 'paid') { + if (employeeAdvance.status === 'repaid') { return Lunas; } @@ -202,7 +202,7 @@ export function createEmployeeAdvanceColumns( ), show: can('employee_advances.pay') && - employeeAdvance.status === 'approved', + (employeeAdvance.status === 'disbursed' || employeeAdvance.status === 'partial'), onClick: () => handlePay(employeeAdvance), }, { diff --git a/tests/Feature/Admin/NotificationTest.php b/tests/Feature/Admin/NotificationTest.php index 50b0768..f54ee3d 100644 --- a/tests/Feature/Admin/NotificationTest.php +++ b/tests/Feature/Admin/NotificationTest.php @@ -522,7 +522,7 @@ function notifEmployeeWithRole(string $role = 'Kasir'): array Notification::assertSentTo($developer, WebPushNotification::class); Notification::assertSentTo($kasbonUser, WebPushNotification::class); - expect($kasbonUser->notifications()->where('title', 'Kasbon Disetujui')->count())->toBe(1); + expect($kasbonUser->notifications()->where('title', 'Kasbon Dikeluarkan')->count())->toBe(1); }); test('EmployeeAdvanceService pay sends notification including the employee', function () { @@ -555,7 +555,7 @@ function notifEmployeeWithRole(string $role = 'Kasir'): array Notification::assertSentTo($developer, WebPushNotification::class); Notification::assertSentTo($kasbonUser, WebPushNotification::class); - expect($kasbonUser->notifications()->where('title', 'Kasbon Dibayar')->count())->toBe(1); + expect($kasbonUser->notifications()->where('title', 'Kasbon Dikembalikan')->count())->toBe(1); }); /*