dstpabuaran.com/app/Services/Admin/Finance/EmployeeAdvanceService.php
Yoga Pangestu 0023309a8f Refactor services to improve role checks and streamline data retrieval
- Updated CustomerService to simplify getAll method.
- Refactored ProductService to utilize HasRoleChecks trait and improved role verification logic.
- Enhanced ProductVariantService with new methods for fetching data for restocking and transactions.
- Cleaned up RawMaterialService by removing unused methods and improving data retrieval.
- Adjusted SupplierService to streamline getAll method.
- Refactored RoleService to use Spatie's Role model and improved role filtering logic.
- Updated NotificationService to handle role labels more effectively.
- Improved StockMutationService by removing redundant paginated method.
- Cleaned up various frontend components to directly accept necessary props instead of nested data objects.
- Updated tests to reflect changes in service method names and ensure proper notification handling.
2026-08-09 11:33:25 +07:00

234 lines
9.4 KiB
PHP

<?php
namespace App\Services\Admin\Finance;
use App\Concerns\HasRoleChecks;
use App\Enums\CashTransactionType;
use App\Enums\EmployeeAdvanceStatus;
use App\Enums\Role;
use App\Models\EmployeeAdvance;
use App\Models\EmployeeAdvancePayment;
use App\Services\Concerns\HandlesCashTransactions;
use App\Services\NotificationService;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
class EmployeeAdvanceService
{
use HandlesCashTransactions, HasRoleChecks;
public function paginated(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc', array $filters = []): LengthAwarePaginator
{
return EmployeeAdvance::query()
->select(['id', 'employee_id', 'amount', 'paid_amount', 'description', 'due_date', 'status', 'created_at'])
->with(['employee.user.userProfile', 'payments.paidBy.userProfile'])
->when(! self::hasAnyRole([Role::DEVELOPER, Role::OWNER, Role::DIREKTUR, Role::ADMIN_TOKO]), fn ($q) => $q->whereHas('employee', fn ($eq) => $eq->where('user_id', auth()->id())))
->when($filters['status'] ?? null, fn ($q) => $q->where('status', $filters['status']))
->when($search, fn ($q) => $q->where('description', 'like', "%{$search}%"))
->orderBy($sort, $direction)
->paginate($perPage);
}
public function store(array $data): EmployeeAdvance
{
$employee = auth()->user()->employee;
if (! $employee) {
throw ValidationException::withMessages([
'amount' => 'Anda tidak terdaftar sebagai karyawan.',
]);
}
$employeeAdvance = EmployeeAdvance::create([
'employee_id' => $employee->id,
'amount' => $data['amount'],
'description' => $data['description'],
'due_date' => $data['due_date'],
'status' => EmployeeAdvanceStatus::PENDING,
]);
$employeeAdvance->load('employee.user');
NotificationService::notify(
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
title: 'Kasbon Baru',
body: 'Kasbon sebesar Rp '.number_format($data['amount'], 0, ',', '.')." dari {$employeeAdvance->employee->name} menunggu persetujuan".' oleh '.auth()->user()->full_name.'.',
url: route('admin.finance.employee-advances.index'),
additionalUser: $employeeAdvance->employee->user ?? null,
);
return $employeeAdvance;
}
public function update(EmployeeAdvance $employeeAdvance, array $data): EmployeeAdvance
{
if ($employeeAdvance->status === EmployeeAdvanceStatus::PAID) {
throw ValidationException::withMessages([
'amount' => 'Kasbon yang sudah dibayar tidak dapat diedit.',
]);
}
if ($employeeAdvance->status === EmployeeAdvanceStatus::APPROVED && $employeeAdvance->cash_transaction_id) {
$oldAmount = $employeeAdvance->amount;
$newAmount = $data['amount'];
if ($oldAmount !== $newAmount) {
$employeeAdvance = DB::transaction(function () use ($employeeAdvance, $data, $oldAmount, $newAmount) {
$this->creditCash(
$oldAmount,
'Pembatalan kasbon: '.$employeeAdvance->description,
CashTransactionType::DEPOSIT,
);
$cashTransaction = $this->debitCash(
$newAmount,
'Kasbon: '.$data['description'],
CashTransactionType::EMPLOYEE_ADVANCE,
);
$employeeAdvance->update([
'amount' => $newAmount,
'description' => $data['description'],
'due_date' => $data['due_date'],
'cash_transaction_id' => $cashTransaction->id,
]);
return $employeeAdvance;
});
return $employeeAdvance;
}
}
$employeeAdvance->update([
'amount' => $data['amount'],
'description' => $data['description'],
'due_date' => $data['due_date'],
]);
return $employeeAdvance;
}
public function destroy(EmployeeAdvance $employeeAdvance): bool
{
return DB::transaction(function () use ($employeeAdvance) {
if ($employeeAdvance->status === EmployeeAdvanceStatus::APPROVED && $employeeAdvance->cash_transaction_id) {
$this->creditCash(
$employeeAdvance->amount,
'Pembatalan kasbon: '.$employeeAdvance->description,
CashTransactionType::DEPOSIT,
);
$employeeAdvance->cashTransaction()->delete();
}
if ($employeeAdvance->status === EmployeeAdvanceStatus::PAID) {
$this->creditCash(
$employeeAdvance->amount,
'Pembatalan kasbon: '.$employeeAdvance->description,
CashTransactionType::DEPOSIT,
);
$employeeAdvance->load('payments');
foreach ($employeeAdvance->payments as $payment) {
if ($payment->cash_transaction_id) {
$this->debitCash(
$payment->amount,
'Pembatalan pembayaran kasbon: '.$employeeAdvance->description,
CashTransactionType::EXPENSE,
);
$payment->cashTransaction()->delete();
}
}
$employeeAdvance->payments()->delete();
}
return $employeeAdvance->delete();
});
}
public function approve(EmployeeAdvance $employeeAdvance): EmployeeAdvance
{
$employeeAdvance = DB::transaction(function () use ($employeeAdvance) {
$cashTransaction = $this->debitCash(
$employeeAdvance->amount,
'Kasbon: '.$employeeAdvance->description,
CashTransactionType::EMPLOYEE_ADVANCE
);
$employeeAdvance->update([
'cash_transaction_id' => $cashTransaction->id,
'status' => EmployeeAdvanceStatus::APPROVED,
'verified_by_id' => auth()->id(),
'verified_at' => now(),
]);
return $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.'.',
url: route('admin.finance.employee-advances.index'),
additionalUser: $employeeAdvance->employee->user ?? null,
);
return $employeeAdvance;
}
public function pay(EmployeeAdvance $employeeAdvance, int $amount): EmployeeAdvance
{
$remaining = $employeeAdvance->amount - $employeeAdvance->paid_amount;
if ($amount > $remaining) {
throw ValidationException::withMessages([
'amount' => 'Jumlah bayar melebihi sisa kasbon.',
]);
}
$employeeAdvance = DB::transaction(function () use ($employeeAdvance, $amount) {
$cashTransaction = $this->creditCash(
$amount,
'Pembayaran kasbon: '.$employeeAdvance->description,
CashTransactionType::DEPOSIT
);
EmployeeAdvancePayment::create([
'employee_advance_id' => $employeeAdvance->id,
'paid_by_id' => auth()->id(),
'cash_transaction_id' => $cashTransaction->id,
'amount' => $amount,
'paid_at' => now(),
]);
$newPaidAmount = $employeeAdvance->paid_amount + $amount;
$isFullyPaid = $newPaidAmount >= $employeeAdvance->amount;
$employeeAdvance->update([
'paid_amount' => $newPaidAmount,
'status' => $isFullyPaid ? EmployeeAdvanceStatus::PAID : $employeeAdvance->status,
'paid_by_id' => $isFullyPaid ? auth()->id() : $employeeAdvance->paid_by_id,
'paid_at' => $isFullyPaid ? now() : $employeeAdvance->paid_at,
]);
return $employeeAdvance;
});
$notificationBody = $employeeAdvance->status === EmployeeAdvanceStatus::PAID
? 'Kasbon sebesar Rp '.number_format($employeeAdvance->amount, 0, ',', '.').' telah dibayar 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',
body: $notificationBody,
url: route('admin.finance.employee-advances.index'),
additionalUser: $employeeAdvance->employee->user ?? null,
);
return $employeeAdvance;
}
}