Files
sam-manage/app/Services/BankAccountService.php
2026-01-20 20:21:06 +09:00

345 lines
9.8 KiB
PHP

<?php
namespace App\Services;
use App\Models\Finance\BankAccount;
use App\Models\Finance\BankTransaction;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class BankAccountService
{
// =========================================================================
// 계좌 목록 조회
// =========================================================================
/**
* 계좌 목록 조회 (페이지네이션)
*/
public function getAccounts(array $filters = [], int $perPage = 15): LengthAwarePaginator
{
$query = BankAccount::query();
// Soft Delete 필터
if (isset($filters['trashed'])) {
if ($filters['trashed'] === 'only') {
$query->onlyTrashed();
} elseif ($filters['trashed'] === 'with') {
$query->withTrashed();
}
}
// 검색 필터
if (! empty($filters['search'])) {
$search = $filters['search'];
$query->where(function ($q) use ($search) {
$q->where('bank_name', 'like', "%{$search}%")
->orWhere('account_number', 'like', "%{$search}%")
->orWhere('account_holder', 'like', "%{$search}%");
});
}
// 은행 필터
if (! empty($filters['bank_name'])) {
$query->where('bank_name', $filters['bank_name']);
}
// 예금종류 필터
if (! empty($filters['account_type'])) {
$query->where('account_type', $filters['account_type']);
}
// 활성 상태 필터 (status 기반)
if (isset($filters['status'])) {
$query->where('status', $filters['status']);
}
return $query
->with(['transactions' => fn($q) => $q->latest()->limit(1)])
->orderBy('sort_order')
->orderBy('bank_name')
->paginate($perPage);
}
/**
* 모든 계좌 목록 (드롭다운용)
*/
public function getAllAccounts(): Collection
{
return BankAccount::active()
->ordered()
->get(['id', 'bank_name', 'account_number', 'account_type', 'balance']);
}
/**
* 은행별 통계
*/
public function getStatsByBank(): Collection
{
return BankAccount::active()
->select('bank_name')
->selectRaw('COUNT(*) as count')
->selectRaw('SUM(balance) as total_balance')
->groupBy('bank_name')
->orderBy('total_balance', 'desc')
->get();
}
// =========================================================================
// 계좌 CRUD
// =========================================================================
/**
* 계좌 상세 조회
*/
public function getAccountById(int $id, bool $withTrashed = false): ?BankAccount
{
$query = BankAccount::query();
if ($withTrashed) {
$query->withTrashed();
}
return $query->find($id);
}
/**
* 계좌 생성
*/
public function createAccount(array $data): BankAccount
{
$data['created_by'] = auth()->id();
$data['tenant_id'] = $data['tenant_id'] ?? session('selected_tenant_id') ?? auth()->user()?->tenant_id;
return BankAccount::create($data);
}
/**
* 계좌 수정
*/
public function updateAccount(BankAccount $account, array $data): BankAccount
{
$data['updated_by'] = auth()->id();
$account->update($data);
return $account->fresh();
}
/**
* 계좌 삭제 (Soft Delete)
*/
public function deleteAccount(BankAccount $account): bool
{
$account->deleted_by = auth()->id();
$account->save();
return $account->delete();
}
/**
* 계좌 복원
*/
public function restoreAccount(BankAccount $account): bool
{
$account->deleted_by = null;
return $account->restore();
}
/**
* 계좌 영구 삭제
*/
public function forceDeleteAccount(BankAccount $account): bool
{
return $account->forceDelete();
}
// =========================================================================
// 계좌 상태 토글
// =========================================================================
/**
* 활성/비활성 토글 (status 기반)
*/
public function toggleActive(BankAccount $account): BankAccount
{
$newStatus = $account->status === 'active' ? 'inactive' : 'active';
$account->update([
'status' => $newStatus,
'updated_by' => auth()->id(),
]);
return $account->fresh();
}
// =========================================================================
// 거래내역 관련
// =========================================================================
/**
* 계좌의 거래내역 조회
*/
public function getTransactions(int $accountId, array $filters = [], int $perPage = 20): LengthAwarePaginator
{
$query = BankTransaction::forAccount($accountId);
// Soft Delete 필터
if (isset($filters['trashed'])) {
if ($filters['trashed'] === 'only') {
$query->onlyTrashed();
} elseif ($filters['trashed'] === 'with') {
$query->withTrashed();
}
}
// 검색 필터
if (! empty($filters['search'])) {
$search = $filters['search'];
$query->where(function ($q) use ($search) {
$q->where('description', 'like', "%{$search}%")
->orWhere('counterparty', 'like', "%{$search}%")
->orWhere('reference_number', 'like', "%{$search}%");
});
}
// 거래유형 필터
if (! empty($filters['transaction_type'])) {
$query->ofType($filters['transaction_type']);
}
// 날짜 범위 필터
if (! empty($filters['start_date']) && ! empty($filters['end_date'])) {
$query->dateBetween($filters['start_date'], $filters['end_date']);
} elseif (! empty($filters['start_date'])) {
$query->where('transaction_date', '>=', $filters['start_date']);
} elseif (! empty($filters['end_date'])) {
$query->where('transaction_date', '<=', $filters['end_date']);
}
// 대사 상태 필터
if (isset($filters['is_reconciled'])) {
$query->reconciled((bool) $filters['is_reconciled']);
}
return $query
->with('bankAccount:id,bank_name,account_number')
->latest()
->paginate($perPage);
}
/**
* 거래내역 생성
*/
public function createTransaction(array $data): BankTransaction
{
return DB::transaction(function () use ($data) {
$account = BankAccount::findOrFail($data['bank_account_id']);
// 거래 후 잔액 계산
$newBalance = match ($data['transaction_type']) {
BankTransaction::TYPE_DEPOSIT => $account->balance + $data['amount'],
BankTransaction::TYPE_WITHDRAWAL, BankTransaction::TYPE_TRANSFER => $account->balance - $data['amount'],
default => $account->balance,
};
$data['balance_after'] = $newBalance;
$data['tenant_id'] = $account->tenant_id;
$data['created_by'] = auth()->id();
$transaction = BankTransaction::create($data);
// 계좌 잔액 업데이트
$account->updateBalance($newBalance);
return $transaction;
});
}
/**
* 거래내역 수정
*/
public function updateTransaction(BankTransaction $transaction, array $data): BankTransaction
{
$data['updated_by'] = auth()->id();
$transaction->update($data);
return $transaction->fresh();
}
/**
* 거래내역 삭제
*/
public function deleteTransaction(BankTransaction $transaction): bool
{
$transaction->deleted_by = auth()->id();
$transaction->save();
return $transaction->delete();
}
// =========================================================================
// 일괄 작업
// =========================================================================
/**
* 일괄 삭제
*/
public function bulkDelete(array $ids): int
{
return BankAccount::whereIn('id', $ids)
->update([
'deleted_by' => auth()->id(),
'deleted_at' => now(),
]);
}
/**
* 일괄 복원
*/
public function bulkRestore(array $ids): int
{
return BankAccount::onlyTrashed()
->whereIn('id', $ids)
->update([
'deleted_by' => null,
'deleted_at' => null,
]);
}
/**
* 일괄 영구 삭제
*/
public function bulkForceDelete(array $ids): int
{
return BankAccount::onlyTrashed()
->whereIn('id', $ids)
->forceDelete();
}
// =========================================================================
// 요약 및 통계
// =========================================================================
/**
* 전체 요약 통계
*/
public function getSummary(): array
{
$accounts = BankAccount::active()->get();
return [
'total_accounts' => $accounts->count(),
'total_balance' => $accounts->sum('balance'),
'formatted_total_balance' => number_format($accounts->sum('balance')) . '원',
'by_bank' => $accounts->groupBy('bank_name')->map(fn($group) => [
'count' => $group->count(),
'total' => $group->sum('balance'),
]),
];
}
}