feat: enhance EmployeeAdvanceService and ExpenseService with validation and transaction handling; update transaction columns for improved display
This commit is contained in:
parent
42e04673c6
commit
8df17601ea
@ -48,7 +48,9 @@ public function create(array $data): EmployeeAdvance
|
|||||||
$employee = auth()->user()->employee;
|
$employee = auth()->user()->employee;
|
||||||
|
|
||||||
if (! $employee) {
|
if (! $employee) {
|
||||||
throw new \Exception('Anda tidak terdaftar sebagai karyawan.');
|
throw ValidationException::withMessages([
|
||||||
|
'amount' => 'Anda tidak terdaftar sebagai karyawan.',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$employeeAdvance = EmployeeAdvance::create([
|
$employeeAdvance = EmployeeAdvance::create([
|
||||||
@ -74,6 +76,44 @@ public function create(array $data): EmployeeAdvance
|
|||||||
|
|
||||||
public function update(EmployeeAdvance $employeeAdvance, array $data): 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([
|
$employeeAdvance->update([
|
||||||
'amount' => $data['amount'],
|
'amount' => $data['amount'],
|
||||||
'description' => $data['description'],
|
'description' => $data['description'],
|
||||||
@ -85,30 +125,60 @@ public function update(EmployeeAdvance $employeeAdvance, array $data): EmployeeA
|
|||||||
|
|
||||||
public function delete(EmployeeAdvance $employeeAdvance): bool
|
public function delete(EmployeeAdvance $employeeAdvance): bool
|
||||||
{
|
{
|
||||||
if ($employeeAdvance->status === EmployeeAdvanceStatus::APPROVED && $employeeAdvance->cash_transaction_id) {
|
return DB::transaction(function () use ($employeeAdvance) {
|
||||||
$cashAccount = $this->getCashAccount();
|
if ($employeeAdvance->status === EmployeeAdvanceStatus::APPROVED && $employeeAdvance->cash_transaction_id) {
|
||||||
$newBalance = $cashAccount->balance + $employeeAdvance->amount;
|
$this->creditCash(
|
||||||
$cashAccount->update(['balance' => $newBalance]);
|
$employeeAdvance->amount,
|
||||||
$employeeAdvance->cashTransaction()->delete();
|
'Pembatalan kasbon: '.$employeeAdvance->description,
|
||||||
}
|
CashTransactionType::DEPOSIT,
|
||||||
|
);
|
||||||
|
$employeeAdvance->cashTransaction()->delete();
|
||||||
|
}
|
||||||
|
|
||||||
return $employeeAdvance->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
|
public function approve(EmployeeAdvance $employeeAdvance): EmployeeAdvance
|
||||||
{
|
{
|
||||||
$cashTransaction = $this->debitCash(
|
$employeeAdvance = DB::transaction(function () use ($employeeAdvance) {
|
||||||
$employeeAdvance->amount,
|
$cashTransaction = $this->debitCash(
|
||||||
'Kasbon: '.$employeeAdvance->description,
|
$employeeAdvance->amount,
|
||||||
CashTransactionType::EMPLOYEE_ADVANCE
|
'Kasbon: '.$employeeAdvance->description,
|
||||||
);
|
CashTransactionType::EMPLOYEE_ADVANCE
|
||||||
|
);
|
||||||
|
|
||||||
$employeeAdvance->update([
|
$employeeAdvance->update([
|
||||||
'cash_transaction_id' => $cashTransaction->id,
|
'cash_transaction_id' => $cashTransaction->id,
|
||||||
'status' => EmployeeAdvanceStatus::APPROVED,
|
'status' => EmployeeAdvanceStatus::APPROVED,
|
||||||
'verified_by_id' => auth()->id(),
|
'verified_by_id' => auth()->id(),
|
||||||
'verified_at' => now(),
|
'verified_at' => now(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
return $employeeAdvance;
|
||||||
|
});
|
||||||
|
|
||||||
NotificationService::notify(
|
NotificationService::notify(
|
||||||
roles: ['Owner', 'Developer', 'Direktur', 'Admin Toko'],
|
roles: ['Owner', 'Developer', 'Direktur', 'Admin Toko'],
|
||||||
@ -135,7 +205,7 @@ public function pay(EmployeeAdvance $employeeAdvance, int $amount): EmployeeAdva
|
|||||||
$cashTransaction = $this->creditCash(
|
$cashTransaction = $this->creditCash(
|
||||||
$amount,
|
$amount,
|
||||||
'Pembayaran kasbon: '.$employeeAdvance->description,
|
'Pembayaran kasbon: '.$employeeAdvance->description,
|
||||||
CashTransactionType::EMPLOYEE_ADVANCE
|
CashTransactionType::DEPOSIT
|
||||||
);
|
);
|
||||||
|
|
||||||
EmployeeAdvancePayment::create([
|
EmployeeAdvancePayment::create([
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Services\Admin\Finance;
|
namespace App\Services\Admin\Finance;
|
||||||
|
|
||||||
|
use App\Enums\CashTransactionType;
|
||||||
use App\Models\CashAccount;
|
use App\Models\CashAccount;
|
||||||
use App\Models\Expense;
|
use App\Models\Expense;
|
||||||
use App\Services\Concerns\HandlesCashTransactions;
|
use App\Services\Concerns\HandlesCashTransactions;
|
||||||
@ -175,10 +176,11 @@ public function update(Expense $expense, array $data): Expense
|
|||||||
public function delete(Expense $expense): bool
|
public function delete(Expense $expense): bool
|
||||||
{
|
{
|
||||||
return DB::transaction(function () use ($expense) {
|
return DB::transaction(function () use ($expense) {
|
||||||
$cashAccount = CashAccount::firstOrFail();
|
$this->creditCash(
|
||||||
|
amount: $expense->amount,
|
||||||
$newBalance = $cashAccount->balance + $expense->amount;
|
description: 'Pembatalan pengeluaran: '.$expense->description,
|
||||||
$cashAccount->update(['balance' => $newBalance]);
|
type: CashTransactionType::DEPOSIT,
|
||||||
|
);
|
||||||
|
|
||||||
// Invalidate receipt cache
|
// Invalidate receipt cache
|
||||||
$media = $expense->getFirstMedia('receipts');
|
$media = $expense->getFirstMedia('receipts');
|
||||||
|
|||||||
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Services\Admin\Finance;
|
namespace App\Services\Admin\Finance;
|
||||||
|
|
||||||
|
use App\Enums\CashTransactionType;
|
||||||
use App\Enums\PayrollPeriodStatus;
|
use App\Enums\PayrollPeriodStatus;
|
||||||
use App\Enums\PayrollStatus;
|
use App\Enums\PayrollStatus;
|
||||||
use App\Models\Payroll;
|
use App\Models\Payroll;
|
||||||
use App\Models\PayrollPeriod;
|
use App\Models\PayrollPeriod;
|
||||||
|
use App\Services\Concerns\HandlesCashTransactions;
|
||||||
use App\Services\NotificationService;
|
use App\Services\NotificationService;
|
||||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@ -14,6 +16,8 @@
|
|||||||
|
|
||||||
class PayrollPeriodService
|
class PayrollPeriodService
|
||||||
{
|
{
|
||||||
|
use HandlesCashTransactions;
|
||||||
|
|
||||||
private function canViewAll(): bool
|
private function canViewAll(): bool
|
||||||
{
|
{
|
||||||
return auth()->user()->hasAnyRole(['developer', 'owner', 'direktur', 'admin-toko']);
|
return auth()->user()->hasAnyRole(['developer', 'owner', 'direktur', 'admin-toko']);
|
||||||
@ -138,9 +142,10 @@ public function pay(Payroll $payroll): Payroll
|
|||||||
}
|
}
|
||||||
|
|
||||||
$payroll = DB::transaction(function () use ($payroll) {
|
$payroll = DB::transaction(function () use ($payroll) {
|
||||||
$cashTransaction = $this->creditCash(
|
$cashTransaction = $this->debitCash(
|
||||||
amount: $payroll->total_amount,
|
amount: $payroll->total_amount,
|
||||||
description: 'Pembayaran gaji karyawan',
|
description: 'Pembayaran gaji karyawan',
|
||||||
|
type: CashTransactionType::EXPENSE,
|
||||||
);
|
);
|
||||||
|
|
||||||
$payroll->update([
|
$payroll->update([
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { ColumnDef } from '@tanstack/react-table';
|
|
||||||
import { Pencil, Trash2 } from 'lucide-react';
|
|
||||||
import { ImagePreviewButton } from '@/components/image-preview-button';
|
import { ImagePreviewButton } from '@/components/image-preview-button';
|
||||||
import { RowActions } from '@/components/row-actions';
|
import { RowActions } from '@/components/row-actions';
|
||||||
|
import type { ColumnDef } from '@tanstack/react-table';
|
||||||
|
import { Pencil, Trash2 } from 'lucide-react';
|
||||||
|
|
||||||
export type CashTransaction = {
|
export type CashTransaction = {
|
||||||
id: number;
|
id: number;
|
||||||
@ -30,23 +30,12 @@ function getTypeLabel(type: string): string {
|
|||||||
deposit: 'Deposit',
|
deposit: 'Deposit',
|
||||||
withdrawal: 'Withdrawal',
|
withdrawal: 'Withdrawal',
|
||||||
expense: 'Pengeluaran',
|
expense: 'Pengeluaran',
|
||||||
transfer: 'Transfer',
|
employee_advance: 'Kasbon',
|
||||||
};
|
};
|
||||||
|
|
||||||
return labels[type] ?? type;
|
return labels[type] ?? type;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getReferenceLabel(type: string): string {
|
|
||||||
const labels: Record<string, string> = {
|
|
||||||
'App\\Models\\Expense': 'Pengeluaran',
|
|
||||||
'App\\Models\\Order': 'Penjualan Tunai',
|
|
||||||
'App\\Models\\Purchase': 'Belanja',
|
|
||||||
'App\\Models\\CashAccount': 'Transfer Kas',
|
|
||||||
};
|
|
||||||
|
|
||||||
return labels[type] ?? '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreateColumnsParams = {
|
type CreateColumnsParams = {
|
||||||
handleEdit: (transaction: CashTransaction) => void;
|
handleEdit: (transaction: CashTransaction) => void;
|
||||||
handleDeleteClick: (transaction: CashTransaction) => void;
|
handleDeleteClick: (transaction: CashTransaction) => void;
|
||||||
|
|||||||
@ -69,8 +69,8 @@ export function createExpenseColumns(
|
|||||||
accessorKey: 'formatted_amount',
|
accessorKey: 'formatted_amount',
|
||||||
header: () => <span>Jumlah</span>,
|
header: () => <span>Jumlah</span>,
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<span className="font-medium text-red-600">
|
<span className="font-medium">
|
||||||
- {row.getValue('formatted_amount') as string}
|
{row.getValue('formatted_amount') as string}
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user