feat: refactor transaction and leave request handling to use centralized action handler

This commit is contained in:
Yoga Pangestu 2026-07-30 10:29:52 +07:00
parent 9033aef5f7
commit 917588eecf
7 changed files with 144 additions and 248 deletions

View File

@ -7,7 +7,6 @@
use App\Models\CashTransaction; use App\Models\CashTransaction;
use App\Services\Admin\Finance\CashAccountService; use App\Services\Admin\Finance\CashAccountService;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia; use Inertia\Inertia;
use Inertia\Response; use Inertia\Response;
@ -29,65 +28,37 @@ public function index(): Response
public function deposit(CashTransactionRequest $request): RedirectResponse public function deposit(CashTransactionRequest $request): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->deposit($request->validated()); fn () => $this->service->deposit($request->validated()),
'Deposit berhasil ditambahkan.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Deposit berhasil ditambahkan.']); 'admin.finance.cash-accounts.index'
} 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 public function withdrawal(CashTransactionRequest $request): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->withdrawal($request->validated()); fn () => $this->service->withdrawal($request->validated()),
'Withdrawal berhasil ditambahkan.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Withdrawal berhasil ditambahkan.']); 'admin.finance.cash-accounts.index'
} 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 public function update(CashTransactionRequest $request, CashTransaction $transaction): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->updateTransaction($transaction, $request->validated()); fn () => $this->service->updateTransaction($transaction, $request->validated()),
'Transaksi berhasil diperbarui.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Transaksi berhasil diperbarui.']); 'admin.finance.cash-accounts.index'
} 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 public function destroy(CashTransaction $transaction): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->deleteTransaction($transaction); fn () => $this->service->deleteTransaction($transaction),
'Transaksi berhasil dihapus.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Transaksi berhasil dihapus.']); 'admin.finance.cash-accounts.index'
} 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,7 +7,6 @@
use App\Models\EmployeeAdvance; use App\Models\EmployeeAdvance;
use App\Services\Admin\Finance\EmployeeAdvanceService; use App\Services\Admin\Finance\EmployeeAdvanceService;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia; use Inertia\Inertia;
use Inertia\Response; use Inertia\Response;
@ -26,72 +25,46 @@ public function index(): Response
public function store(EmployeeAdvanceRequest $request): RedirectResponse public function store(EmployeeAdvanceRequest $request): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->create($request->validated()); fn () => $this->service->create($request->validated()),
'Kasbon berhasil ditambahkan.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Kasbon berhasil ditambahkan.']); 'admin.finance.employee-advances.index'
} 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.employee-advances.index');
} }
public function update(EmployeeAdvanceRequest $request, EmployeeAdvance $employeeAdvance): RedirectResponse public function update(EmployeeAdvanceRequest $request, EmployeeAdvance $employeeAdvance): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->update($employeeAdvance, $request->validated()); fn () => $this->service->update($employeeAdvance, $request->validated()),
'Kasbon berhasil diperbarui.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Kasbon berhasil diperbarui.']); 'admin.finance.employee-advances.index'
} 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.employee-advances.index');
} }
public function destroy(EmployeeAdvance $employeeAdvance): RedirectResponse public function destroy(EmployeeAdvance $employeeAdvance): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->delete($employeeAdvance); fn () => $this->service->delete($employeeAdvance),
'Kasbon berhasil dihapus.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Kasbon berhasil dihapus.']); 'admin.finance.employee-advances.index'
} catch (\Exception $e) { );
Inertia::flash('toast', ['type' => 'error', 'message' => $e->getMessage()]);
}
return to_route('admin.finance.employee-advances.index');
} }
public function approve(EmployeeAdvance $employeeAdvance): RedirectResponse public function approve(EmployeeAdvance $employeeAdvance): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->approve($employeeAdvance); fn () => $this->service->approve($employeeAdvance),
'Kasbon berhasil disetujui.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Kasbon berhasil disetujui.']); 'admin.finance.employee-advances.index'
} catch (\Exception $e) { );
Inertia::flash('toast', ['type' => 'error', 'message' => $e->getMessage()]);
}
return to_route('admin.finance.employee-advances.index');
} }
public function pay(EmployeeAdvance $employeeAdvance): RedirectResponse public function pay(EmployeeAdvance $employeeAdvance): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->pay($employeeAdvance); fn () => $this->service->pay($employeeAdvance),
'Kasbon berhasil dibayar.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Kasbon berhasil dibayar.']); 'admin.finance.employee-advances.index'
} catch (\Exception $e) { );
Inertia::flash('toast', ['type' => 'error', 'message' => $e->getMessage()]);
}
return to_route('admin.finance.employee-advances.index');
} }
} }

View File

@ -7,7 +7,6 @@
use App\Models\Expense; use App\Models\Expense;
use App\Services\Admin\Finance\ExpenseService; use App\Services\Admin\Finance\ExpenseService;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia; use Inertia\Inertia;
use Inertia\Response; use Inertia\Response;
@ -26,46 +25,28 @@ public function index(): Response
public function store(ExpenseRequest $request): RedirectResponse public function store(ExpenseRequest $request): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->create($request->validated()); fn () => $this->service->create($request->validated()),
'Pengeluaran berhasil ditambahkan.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Pengeluaran berhasil ditambahkan.']); 'admin.finance.expenses.index'
} 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 public function update(ExpenseRequest $request, Expense $expense): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->update($expense, $request->validated()); fn () => $this->service->update($expense, $request->validated()),
'Pengeluaran berhasil diperbarui.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Pengeluaran berhasil diperbarui.']); 'admin.finance.expenses.index'
} 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 public function destroy(Expense $expense): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->delete($expense); fn () => $this->service->delete($expense),
'Pengeluaran berhasil dihapus.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Pengeluaran berhasil dihapus.']); 'admin.finance.expenses.index'
} catch (\Exception $e) { );
Inertia::flash('toast', ['type' => 'error', 'message' => $e->getMessage()]);
}
return to_route('admin.finance.expenses.index');
} }
} }

View File

@ -7,7 +7,6 @@
use App\Models\User; use App\Models\User;
use App\Services\Admin\HR\EmployeeService; use App\Services\Admin\HR\EmployeeService;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia; use Inertia\Inertia;
use Inertia\Response; use Inertia\Response;
@ -31,18 +30,11 @@ public function create(): Response
public function store(EmployeeRequest $request): RedirectResponse public function store(EmployeeRequest $request): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->create($request->validated()); fn () => $this->service->create($request->validated()),
'Pegawai berhasil ditambahkan.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Pegawai berhasil ditambahkan.']); 'admin.hr.employees.index'
} 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.hr.employees.index');
} }
public function edit(User $employee): Response public function edit(User $employee): Response
@ -54,66 +46,38 @@ public function edit(User $employee): Response
public function update(EmployeeRequest $request, User $employee): RedirectResponse public function update(EmployeeRequest $request, User $employee): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->update($employee, $request->validated()); fn () => $this->service->update($employee, $request->validated()),
'Pegawai berhasil diperbarui.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Pegawai berhasil diperbarui.']); 'admin.hr.employees.index'
} 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.hr.employees.index');
} }
public function destroy(User $employee): RedirectResponse public function destroy(User $employee): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->delete($employee); fn () => $this->service->delete($employee),
'Pegawai berhasil dihapus.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Pegawai berhasil dihapus.']); 'admin.hr.employees.index'
} 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.hr.employees.index');
} }
public function toggleActive(User $employee): RedirectResponse public function toggleActive(User $employee): RedirectResponse
{ {
try { $this->service->toggleActive($employee);
$this->service->toggleActive($employee); $status = $employee->fresh()->is_active ? 'diaktifkan' : 'dinonaktifkan';
$status = $employee->fresh()->is_active ? 'diaktifkan' : 'dinonaktifkan'; Inertia::flash('toast', ['type' => 'success', 'message' => "Pegawai berhasil {$status}."]);
Inertia::flash('toast', ['type' => 'success', 'message' => "Pegawai berhasil {$status}."]);
} 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.hr.employees.index'); return to_route('admin.hr.employees.index');
} }
public function resetPassword(User $employee): RedirectResponse public function resetPassword(User $employee): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->resetPassword($employee); fn () => $this->service->resetPassword($employee),
'Kata sandi pegawai berhasil direset ke kata sandi default.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Kata sandi pegawai berhasil direset ke kata sandi default.']); 'admin.hr.employees.index'
} 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.hr.employees.index');
} }
} }

View File

@ -7,7 +7,6 @@
use App\Models\LeaveRequest; use App\Models\LeaveRequest;
use App\Services\Admin\HR\LeaveRequestService; use App\Services\Admin\HR\LeaveRequestService;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia; use Inertia\Inertia;
use Inertia\Response; use Inertia\Response;
@ -26,72 +25,46 @@ public function index(): Response
public function store(LeaveRequestRequest $request): RedirectResponse public function store(LeaveRequestRequest $request): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->create($request->validated()); fn () => $this->service->create($request->validated()),
'Permohonan cuti berhasil ditambahkan.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Permohonan cuti berhasil ditambahkan.']); 'admin.hr.leave-requests.index'
} 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.hr.leave-requests.index');
} }
public function update(LeaveRequestRequest $request, LeaveRequest $leaveRequest): RedirectResponse public function update(LeaveRequestRequest $request, LeaveRequest $leaveRequest): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->update($leaveRequest, $request->validated()); fn () => $this->service->update($leaveRequest, $request->validated()),
'Permohonan cuti berhasil diperbarui.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Permohonan cuti berhasil diperbarui.']); 'admin.hr.leave-requests.index'
} 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.hr.leave-requests.index');
} }
public function destroy(LeaveRequest $leaveRequest): RedirectResponse public function destroy(LeaveRequest $leaveRequest): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->delete($leaveRequest); fn () => $this->service->delete($leaveRequest),
'Permohonan cuti berhasil dihapus.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Permohonan cuti berhasil dihapus.']); 'admin.hr.leave-requests.index'
} catch (\Exception $e) { );
Inertia::flash('toast', ['type' => 'error', 'message' => $e->getMessage()]);
}
return to_route('admin.hr.leave-requests.index');
} }
public function approve(LeaveRequest $leaveRequest): RedirectResponse public function approve(LeaveRequest $leaveRequest): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->approve($leaveRequest); fn () => $this->service->approve($leaveRequest),
'Permohonan cuti berhasil disetujui.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Permohonan cuti berhasil disetujui.']); 'admin.hr.leave-requests.index'
} catch (\Exception $e) { );
Inertia::flash('toast', ['type' => 'error', 'message' => $e->getMessage()]);
}
return to_route('admin.hr.leave-requests.index');
} }
public function reject(LeaveRequest $leaveRequest): RedirectResponse public function reject(LeaveRequest $leaveRequest): RedirectResponse
{ {
try { return $this->handleAction(
$this->service->reject($leaveRequest); fn () => $this->service->reject($leaveRequest),
'Permohonan cuti berhasil ditolak.',
Inertia::flash('toast', ['type' => 'success', 'message' => 'Permohonan cuti berhasil ditolak.']); 'admin.hr.leave-requests.index'
} catch (\Exception $e) { );
Inertia::flash('toast', ['type' => 'error', 'message' => $e->getMessage()]);
}
return to_route('admin.hr.leave-requests.index');
} }
} }

View File

@ -2,7 +2,24 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia;
abstract class Controller abstract class Controller
{ {
// protected function handleAction(callable $action, string $successMessage, string $redirectRoute): RedirectResponse
{
try {
$action();
Inertia::flash('toast', ['type' => 'success', 'message' => $successMessage]);
} 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($redirectRoute);
}
} }

View File

@ -8,6 +8,7 @@
use App\Models\Expense; use App\Models\Expense;
use App\Services\S3PresignedService; use App\Services\S3PresignedService;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
@ -46,9 +47,14 @@ private function formatExpense(Expense $expense): array
$s3Key = $media->getPath(); $s3Key = $media->getPath();
} }
$cacheKey = "expense_receipt_{$media->id}";
$receiptUrl = Cache::remember($cacheKey, now()->addMinutes(55), function () use ($s3Key) {
return $this->s3Service->getTemporaryUrl($s3Key);
});
return $expense->toArray() + [ return $expense->toArray() + [
'receipt_key' => $s3Key, 'receipt_key' => $s3Key,
'receipt_url' => $this->s3Service->getTemporaryUrl($s3Key), 'receipt_url' => $receiptUrl,
]; ];
} }
@ -130,6 +136,11 @@ public function update(Expense $expense, array $data): Expense
} }
if ($data['receipt_key'] !== $currentKey) { if ($data['receipt_key'] !== $currentKey) {
// Invalidate old receipt cache
if ($currentMedia) {
Cache::forget("expense_receipt_{$currentMedia->id}");
}
$expense->clearMediaCollection('receipts'); $expense->clearMediaCollection('receipts');
if (! empty($data['receipt_key'])) { if (! empty($data['receipt_key'])) {
@ -150,6 +161,12 @@ public function delete(Expense $expense): bool
$newBalance = $cashAccount->balance + $expense->amount; $newBalance = $cashAccount->balance + $expense->amount;
$cashAccount->update(['balance' => $newBalance]); $cashAccount->update(['balance' => $newBalance]);
// Invalidate receipt cache
$media = $expense->getFirstMedia('receipts');
if ($media) {
Cache::forget("expense_receipt_{$media->id}");
}
$expense->clearMediaCollection('receipts'); $expense->clearMediaCollection('receipts');
return $expense->delete(); return $expense->delete();