234 lines
9.5 KiB
PHP
234 lines
9.5 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::REPAID) {
|
|
throw ValidationException::withMessages([
|
|
'amount' => 'Kasbon yang sudah dikembalikan tidak dapat diedit.',
|
|
]);
|
|
}
|
|
|
|
if ($employeeAdvance->status === EmployeeAdvanceStatus::DISBURSED && $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::DISBURSED && $employeeAdvance->cash_transaction_id) {
|
|
$this->creditCash(
|
|
$employeeAdvance->amount,
|
|
'Pembatalan kasbon: '.$employeeAdvance->description,
|
|
CashTransactionType::DEPOSIT,
|
|
);
|
|
$employeeAdvance->cashTransaction()->delete();
|
|
}
|
|
|
|
if ($employeeAdvance->status === EmployeeAdvanceStatus::REPAID || $employeeAdvance->status === EmployeeAdvanceStatus::PARTIAL) {
|
|
$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::DISBURSED,
|
|
'verified_by_id' => auth()->id(),
|
|
'verified_at' => now(),
|
|
]);
|
|
|
|
return $employeeAdvance;
|
|
});
|
|
|
|
NotificationService::notify(
|
|
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
|
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,
|
|
);
|
|
|
|
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::REPAID : EmployeeAdvanceStatus::PARTIAL,
|
|
'paid_by_id' => $isFullyPaid ? auth()->id() : $employeeAdvance->paid_by_id,
|
|
'paid_at' => $isFullyPaid ? now() : $employeeAdvance->paid_at,
|
|
]);
|
|
|
|
return $employeeAdvance;
|
|
});
|
|
|
|
$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::REPAID ? 'Kasbon Dikembalikan' : 'Pembayaran Kasbon',
|
|
body: $notificationBody,
|
|
url: route('admin.finance.employee-advances.index'),
|
|
additionalUser: $employeeAdvance->employee->user ?? null,
|
|
);
|
|
|
|
return $employeeAdvance;
|
|
}
|
|
}
|