with(['createdBy.profile', 'media']) ->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void { $search = $tableQuery['search']; $query->where(function (Builder $query) use ($search): void { $query->where('description', 'like', "%{$search}%"); }); }); $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query ->paginate(25) ->withQueryString() ->through(function (Expense $expense) { $expense->setAttribute( 'photos', MediaPresenter::collection($expense, 'photos'), ); return $expense; }); } public function create(array $validated, User $user): void { $expense = $this->runInTransaction( function () use ($validated, $user): Expense { $amount = (int) $validated['amount']; $description = $validated['description']; $expense = Expense::create([ 'amount' => $amount, 'description' => $description, 'created_by_id' => $user->id, ]); $cashTransaction = $this->cashService->recordOutgoing( $expense, $amount, $description, $user, ); $expense->update([ 'cash_transaction_id' => $cashTransaction->id, ]); $this->syncPhotos($expense, $validated, self::MAX_PHOTOS); return $expense; }, 'Gagal membuat pengeluaran', ); $this->pushNotificationService->sendToRoles( '💸 Pengeluaran Baru', "Pengeluaran baru sebesar {$expense->amount_formatted} dengan keterangan {$expense->description} telah dicatat oleh {$user->profile?->full_name}.", ['owner', 'developer', 'direktur'], route('admin.finance.expenses.index'), ); } public function update(Expense $expense, array $validated, User $user): void { $this->runInTransaction( function () use ($expense, $validated): void { $amount = (int) $validated['amount']; $description = $validated['description']; $expense->update([ 'amount' => $amount, 'description' => $description, ]); if ($expense->cashTransaction) { $this->cashService->updateReferencedTransaction( $expense->cashTransaction, $amount, $description, ); } $this->syncPhotos($expense, $validated, self::MAX_PHOTOS); }, 'Gagal memperbarui pengeluaran', ); $this->pushNotificationService->sendToRoles( '✏️ Pengeluaran Diperbarui', "Pengeluaran dengan keterangan {$expense->description} diperbarui menjadi sebesar {$expense->amount_formatted} oleh {$user->profile?->full_name}.", ['owner', 'developer', 'direktur'], route('admin.finance.expenses.index'), ); } public function delete(Expense $expense, User $user): void { $amountFormatted = $expense->amount_formatted; $description = $expense->description; $this->runInTransaction( function () use ($expense): void { if ($expense->cashTransaction) { $this->cashService->deleteReferencedTransaction($expense->cashTransaction); } $expense->clearMediaCollection('photos'); $expense->delete(); }, 'Gagal menghapus pengeluaran', ); $this->pushNotificationService->sendToRoles( '🗑️ Pengeluaran Dihapus', "Pengeluaran sebesar {$amountFormatted} dengan keterangan {$description} telah dihapus oleh {$user->profile?->full_name}.", ['owner', 'developer', 'direktur'], route('admin.finance.expenses.index'), ); } private function applySorting(Builder $query, string $sort, string $direction): void { if (in_array($sort, ['created_at', 'amount', 'description'], true)) { $query->orderBy($sort, $direction); return; } $query->latest(); } }