feat: enhance transaction handling with error management and database transactions in CashAccountService and controllers

This commit is contained in:
Yoga Pangestu 2026-07-29 21:07:30 +07:00
parent 912f576c74
commit d1f1d82099
3 changed files with 143 additions and 86 deletions

View File

@ -7,6 +7,7 @@
use App\Models\CashTransaction;
use App\Services\Admin\Finance\CashAccountService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia;
use Inertia\Response;
@ -28,36 +29,64 @@ public function index(): Response
public function deposit(CashTransactionRequest $request): RedirectResponse
{
$this->service->deposit($request->validated());
try {
$this->service->deposit($request->validated());
Inertia::flash('toast', ['type' => 'success', 'message' => 'Deposit berhasil ditambahkan.']);
Inertia::flash('toast', ['type' => 'success', 'message' => 'Deposit berhasil ditambahkan.']);
} catch (ValidationException $e) {
$firstError = collect($e->errors())->flatten()->first();
Inertia::flash('toast', ['type' => 'error', 'message' => $firstError ?? 'Terjadi kesalahan.']);
} catch (\Exception $e) {
Inertia::flash('toast', ['type' => 'error', 'message' => $e->getMessage()]);
}
return to_route('admin.finance.cash-accounts.index');
}
public function withdrawal(CashTransactionRequest $request): RedirectResponse
{
$this->service->withdrawal($request->validated());
try {
$this->service->withdrawal($request->validated());
Inertia::flash('toast', ['type' => 'success', 'message' => 'Withdrawal berhasil ditambahkan.']);
Inertia::flash('toast', ['type' => 'success', 'message' => 'Withdrawal berhasil ditambahkan.']);
} catch (ValidationException $e) {
$firstError = collect($e->errors())->flatten()->first();
Inertia::flash('toast', ['type' => 'error', 'message' => $firstError ?? 'Terjadi kesalahan.']);
} catch (\Exception $e) {
Inertia::flash('toast', ['type' => 'error', 'message' => $e->getMessage()]);
}
return to_route('admin.finance.cash-accounts.index');
}
public function update(CashTransactionRequest $request, CashTransaction $transaction): RedirectResponse
{
$this->service->updateTransaction($transaction, $request->validated());
try {
$this->service->updateTransaction($transaction, $request->validated());
Inertia::flash('toast', ['type' => 'success', 'message' => 'Transaksi berhasil diperbarui.']);
Inertia::flash('toast', ['type' => 'success', 'message' => 'Transaksi berhasil diperbarui.']);
} catch (ValidationException $e) {
$firstError = collect($e->errors())->flatten()->first();
Inertia::flash('toast', ['type' => 'error', 'message' => $firstError ?? 'Terjadi kesalahan.']);
} catch (\Exception $e) {
Inertia::flash('toast', ['type' => 'error', 'message' => $e->getMessage()]);
}
return to_route('admin.finance.cash-accounts.index');
}
public function destroy(CashTransaction $transaction): RedirectResponse
{
$this->service->deleteTransaction($transaction);
try {
$this->service->deleteTransaction($transaction);
Inertia::flash('toast', ['type' => 'success', 'message' => 'Transaksi berhasil dihapus.']);
Inertia::flash('toast', ['type' => 'success', 'message' => 'Transaksi berhasil dihapus.']);
} catch (ValidationException $e) {
$firstError = collect($e->errors())->flatten()->first();
Inertia::flash('toast', ['type' => 'error', 'message' => $firstError ?? 'Terjadi kesalahan.']);
} catch (\Exception $e) {
Inertia::flash('toast', ['type' => 'error', 'message' => $e->getMessage()]);
}
return to_route('admin.finance.cash-accounts.index');
}

View File

@ -7,6 +7,7 @@
use App\Models\Expense;
use App\Services\Admin\Finance\ExpenseService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia;
use Inertia\Response;
@ -25,27 +26,45 @@ public function index(): Response
public function store(ExpenseRequest $request): RedirectResponse
{
$this->service->create($request->validated());
try {
$this->service->create($request->validated());
Inertia::flash('toast', ['type' => 'success', 'message' => 'Pengeluaran berhasil ditambahkan.']);
Inertia::flash('toast', ['type' => 'success', 'message' => 'Pengeluaran berhasil ditambahkan.']);
} catch (ValidationException $e) {
$firstError = collect($e->errors())->flatten()->first();
Inertia::flash('toast', ['type' => 'error', 'message' => $firstError ?? 'Terjadi kesalahan.']);
} catch (\Exception $e) {
Inertia::flash('toast', ['type' => 'error', 'message' => $e->getMessage()]);
}
return to_route('admin.finance.expenses.index');
}
public function update(ExpenseRequest $request, Expense $expense): RedirectResponse
{
$this->service->update($expense, $request->validated());
try {
$this->service->update($expense, $request->validated());
Inertia::flash('toast', ['type' => 'success', 'message' => 'Pengeluaran berhasil diperbarui.']);
Inertia::flash('toast', ['type' => 'success', 'message' => 'Pengeluaran berhasil diperbarui.']);
} catch (ValidationException $e) {
$firstError = collect($e->errors())->flatten()->first();
Inertia::flash('toast', ['type' => 'error', 'message' => $firstError ?? 'Terjadi kesalahan.']);
} catch (\Exception $e) {
Inertia::flash('toast', ['type' => 'error', 'message' => $e->getMessage()]);
}
return to_route('admin.finance.expenses.index');
}
public function destroy(Expense $expense): RedirectResponse
{
$this->service->delete($expense);
try {
$this->service->delete($expense);
Inertia::flash('toast', ['type' => 'success', 'message' => 'Pengeluaran berhasil dihapus.']);
Inertia::flash('toast', ['type' => 'success', 'message' => 'Pengeluaran berhasil dihapus.']);
} catch (\Exception $e) {
Inertia::flash('toast', ['type' => 'error', 'message' => $e->getMessage()]);
}
return to_route('admin.finance.expenses.index');
}

View File

@ -6,6 +6,7 @@
use App\Models\CashAccount;
use App\Models\CashTransaction;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
class CashAccountService
@ -31,100 +32,108 @@ public function getAllTransactions(): Collection
public function deposit(array $data): CashTransaction
{
$cashAccount = CashAccount::firstOrFail();
$newBalance = $cashAccount->balance + $data['amount'];
return DB::transaction(function () use ($data) {
$cashAccount = CashAccount::firstOrFail();
$newBalance = $cashAccount->balance + $data['amount'];
$cashAccount->update(['balance' => $newBalance]);
$cashAccount->update(['balance' => $newBalance]);
return CashTransaction::create([
'cash_account_id' => $cashAccount->id,
'created_by_id' => auth()->id(),
'amount' => $data['amount'],
'balance_after' => $newBalance,
'type' => CashTransactionType::DEPOSIT,
'description' => $data['description'],
]);
return CashTransaction::create([
'cash_account_id' => $cashAccount->id,
'created_by_id' => auth()->id(),
'amount' => $data['amount'],
'balance_after' => $newBalance,
'type' => CashTransactionType::DEPOSIT,
'description' => $data['description'],
]);
});
}
public function withdrawal(array $data): CashTransaction
{
$cashAccount = CashAccount::firstOrFail();
return DB::transaction(function () use ($data) {
$cashAccount = CashAccount::firstOrFail();
if ($cashAccount->balance < $data['amount']) {
throw ValidationException::withMessages([
'amount' => 'Saldo tidak mencukupi.',
if ($cashAccount->balance < $data['amount']) {
throw ValidationException::withMessages([
'amount' => 'Saldo tidak mencukupi.',
]);
}
$newBalance = $cashAccount->balance - $data['amount'];
$cashAccount->update(['balance' => $newBalance]);
return CashTransaction::create([
'cash_account_id' => $cashAccount->id,
'created_by_id' => auth()->id(),
'amount' => $data['amount'],
'balance_after' => $newBalance,
'type' => CashTransactionType::WITHDRAWAL,
'description' => $data['description'],
]);
}
$newBalance = $cashAccount->balance - $data['amount'];
$cashAccount->update(['balance' => $newBalance]);
return CashTransaction::create([
'cash_account_id' => $cashAccount->id,
'created_by_id' => auth()->id(),
'amount' => $data['amount'],
'balance_after' => $newBalance,
'type' => CashTransactionType::WITHDRAWAL,
'description' => $data['description'],
]);
});
}
public function updateTransaction(CashTransaction $transaction, array $data): CashTransaction
{
if (! in_array($transaction->type, [CashTransactionType::DEPOSIT, CashTransactionType::WITHDRAWAL])) {
throw ValidationException::withMessages([
'amount' => 'Transaksi ini tidak dapat diedit.',
return DB::transaction(function () use ($transaction, $data) {
if (! in_array($transaction->type, [CashTransactionType::DEPOSIT, CashTransactionType::WITHDRAWAL])) {
throw ValidationException::withMessages([
'amount' => 'Transaksi ini tidak dapat diedit.',
]);
}
$cashAccount = CashAccount::firstOrFail();
$oldAmount = $transaction->amount;
$newAmount = $data['amount'];
$isDeposit = $transaction->type === CashTransactionType::DEPOSIT;
$difference = $isDeposit ? $newAmount - $oldAmount : $oldAmount - $newAmount;
if ($difference < 0 && $cashAccount->balance < abs($difference)) {
throw ValidationException::withMessages([
'amount' => 'Saldo tidak mencukupi.',
]);
}
$newBalance = $cashAccount->balance + $difference;
$cashAccount->update(['balance' => $newBalance]);
$transaction->update([
'amount' => $newAmount,
'balance_after' => $newBalance,
'description' => $data['description'],
]);
}
$cashAccount = CashAccount::firstOrFail();
$oldAmount = $transaction->amount;
$newAmount = $data['amount'];
$isDeposit = $transaction->type === CashTransactionType::DEPOSIT;
$difference = $isDeposit ? $newAmount - $oldAmount : $oldAmount - $newAmount;
if ($difference < 0 && $cashAccount->balance < abs($difference)) {
throw ValidationException::withMessages([
'amount' => 'Saldo tidak mencukupi.',
]);
}
$newBalance = $cashAccount->balance + $difference;
$cashAccount->update(['balance' => $newBalance]);
$transaction->update([
'amount' => $newAmount,
'balance_after' => $newBalance,
'description' => $data['description'],
]);
return $transaction;
return $transaction;
});
}
public function deleteTransaction(CashTransaction $transaction): bool
{
if (! in_array($transaction->type, [CashTransactionType::DEPOSIT, CashTransactionType::WITHDRAWAL])) {
throw ValidationException::withMessages([
'amount' => 'Transaksi ini tidak dapat dihapus.',
]);
}
return DB::transaction(function () use ($transaction) {
if (! in_array($transaction->type, [CashTransactionType::DEPOSIT, CashTransactionType::WITHDRAWAL])) {
throw ValidationException::withMessages([
'amount' => 'Transaksi ini tidak dapat dihapus.',
]);
}
$cashAccount = CashAccount::firstOrFail();
$isDeposit = $transaction->type === CashTransactionType::DEPOSIT;
$cashAccount = CashAccount::firstOrFail();
$isDeposit = $transaction->type === CashTransactionType::DEPOSIT;
$newBalance = $isDeposit
? $cashAccount->balance - $transaction->amount
: $cashAccount->balance + $transaction->amount;
$newBalance = $isDeposit
? $cashAccount->balance - $transaction->amount
: $cashAccount->balance + $transaction->amount;
if ($newBalance < 0) {
throw ValidationException::withMessages([
'amount' => 'Saldo tidak mencukupi untuk menghapus transaksi ini.',
]);
}
if ($newBalance < 0) {
throw ValidationException::withMessages([
'amount' => 'Saldo tidak mencukupi untuk menghapus transaksi ini.',
]);
}
$cashAccount->update(['balance' => $newBalance]);
$cashAccount->update(['balance' => $newBalance]);
return $transaction->delete();
return $transaction->delete();
});
}
}