feat: update EmployeeAdvance status values and related logic, modify notifications accordingly
This commit is contained in:
parent
7d21a1e6db
commit
b905c0a2c6
@ -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 |
|
||||
|
||||
@ -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',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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']),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <span className="text-green-600">Lunas</span>;
|
||||
}
|
||||
|
||||
@ -202,7 +202,7 @@ export function createEmployeeAdvanceColumns(
|
||||
),
|
||||
show:
|
||||
can('employee_advances.pay') &&
|
||||
employeeAdvance.status === 'approved',
|
||||
(employeeAdvance.status === 'disbursed' || employeeAdvance.status === 'partial'),
|
||||
onClick: () => handlePay(employeeAdvance),
|
||||
},
|
||||
{
|
||||
|
||||
@ -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);
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
Loading…
Reference in New Issue
Block a user