2025-12-17 21:02:20 +09:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
|
|
|
|
|
|
use App\Models\Tenants\BankAccount;
|
|
|
|
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
|
|
|
|
|
|
class BankAccountService extends Service
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* 계좌 목록 조회
|
|
|
|
|
*/
|
|
|
|
|
public function index(array $params): LengthAwarePaginator
|
|
|
|
|
{
|
|
|
|
|
$tenantId = $this->tenantId();
|
|
|
|
|
|
|
|
|
|
$query = BankAccount::query()
|
|
|
|
|
->where('tenant_id', $tenantId);
|
|
|
|
|
|
|
|
|
|
// 검색 필터
|
|
|
|
|
if (! empty($params['search'])) {
|
|
|
|
|
$search = $params['search'];
|
|
|
|
|
$query->where(function ($q) use ($search) {
|
|
|
|
|
$q->where('account_name', 'like', "%{$search}%")
|
|
|
|
|
->orWhere('bank_name', 'like', "%{$search}%")
|
|
|
|
|
->orWhere('account_holder', 'like', "%{$search}%")
|
|
|
|
|
->orWhere('account_number', 'like', "%{$search}%");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 상태 필터
|
|
|
|
|
if (! empty($params['status'])) {
|
|
|
|
|
$query->where('status', $params['status']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 담당자 필터
|
|
|
|
|
if (! empty($params['assigned_user_id'])) {
|
|
|
|
|
$query->where('assigned_user_id', $params['assigned_user_id']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 대표계좌만 필터
|
|
|
|
|
if (isset($params['is_primary']) && $params['is_primary']) {
|
|
|
|
|
$query->where('is_primary', true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 정렬: 대표계좌 먼저, 그 다음 생성일순
|
|
|
|
|
$query->orderByDesc('is_primary')
|
|
|
|
|
->orderBy($params['sort_by'] ?? 'created_at', $params['sort_dir'] ?? 'desc');
|
|
|
|
|
|
|
|
|
|
// 페이지네이션
|
|
|
|
|
$perPage = $params['per_page'] ?? 20;
|
|
|
|
|
|
|
|
|
|
return $query->paginate($perPage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 계좌 상세 조회
|
|
|
|
|
*/
|
|
|
|
|
public function show(int $id): BankAccount
|
|
|
|
|
{
|
|
|
|
|
$tenantId = $this->tenantId();
|
|
|
|
|
|
|
|
|
|
return BankAccount::query()
|
|
|
|
|
->where('tenant_id', $tenantId)
|
|
|
|
|
->with(['assignedUser:id,name'])
|
|
|
|
|
->findOrFail($id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 계좌 등록
|
|
|
|
|
*/
|
|
|
|
|
public function store(array $data): BankAccount
|
|
|
|
|
{
|
|
|
|
|
$tenantId = $this->tenantId();
|
|
|
|
|
$userId = $this->apiUserId();
|
|
|
|
|
|
|
|
|
|
return DB::transaction(function () use ($data, $tenantId, $userId) {
|
|
|
|
|
// 첫 번째 계좌인 경우 자동으로 대표계좌 설정
|
|
|
|
|
$isFirst = BankAccount::where('tenant_id', $tenantId)->count() === 0;
|
|
|
|
|
$isPrimary = $data['is_primary'] ?? $isFirst;
|
|
|
|
|
|
|
|
|
|
// 대표계좌로 설정 시 기존 대표계좌 해제
|
|
|
|
|
if ($isPrimary) {
|
|
|
|
|
BankAccount::where('tenant_id', $tenantId)
|
|
|
|
|
->where('is_primary', true)
|
|
|
|
|
->update(['is_primary' => false]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$account = BankAccount::create([
|
|
|
|
|
'tenant_id' => $tenantId,
|
|
|
|
|
'bank_code' => $data['bank_code'],
|
|
|
|
|
'bank_name' => $data['bank_name'],
|
|
|
|
|
'account_number' => $data['account_number'],
|
|
|
|
|
'account_holder' => $data['account_holder'],
|
|
|
|
|
'account_name' => $data['account_name'],
|
fix: [bank-account] 계좌 관리 API 누락 필드 8개 보강
- account_type, balance, currency, opened_at, branch_name, memo, sort_order, last_transaction_at 추가
- Model: fillable, casts, hidden, scopes, accessors를 MNG 모델 기준으로 통일
- Service: store/update에 누락 필드 반영
- FormRequest: Store/Update에 검증 규칙 추가
2026-02-20 23:20:49 +09:00
|
|
|
'account_type' => $data['account_type'] ?? null,
|
|
|
|
|
'balance' => $data['balance'] ?? 0,
|
|
|
|
|
'currency' => $data['currency'] ?? 'KRW',
|
|
|
|
|
'opened_at' => $data['opened_at'] ?? null,
|
|
|
|
|
'branch_name' => $data['branch_name'] ?? null,
|
|
|
|
|
'memo' => $data['memo'] ?? null,
|
2025-12-17 21:02:20 +09:00
|
|
|
'status' => $data['status'] ?? 'active',
|
|
|
|
|
'assigned_user_id' => $data['assigned_user_id'] ?? null,
|
|
|
|
|
'is_primary' => $isPrimary,
|
fix: [bank-account] 계좌 관리 API 누락 필드 8개 보강
- account_type, balance, currency, opened_at, branch_name, memo, sort_order, last_transaction_at 추가
- Model: fillable, casts, hidden, scopes, accessors를 MNG 모델 기준으로 통일
- Service: store/update에 누락 필드 반영
- FormRequest: Store/Update에 검증 규칙 추가
2026-02-20 23:20:49 +09:00
|
|
|
'sort_order' => $data['sort_order'] ?? 0,
|
2025-12-17 21:02:20 +09:00
|
|
|
'created_by' => $userId,
|
|
|
|
|
'updated_by' => $userId,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return $account;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 계좌 수정
|
|
|
|
|
*/
|
|
|
|
|
public function update(int $id, array $data): BankAccount
|
|
|
|
|
{
|
|
|
|
|
$tenantId = $this->tenantId();
|
|
|
|
|
$userId = $this->apiUserId();
|
|
|
|
|
|
|
|
|
|
return DB::transaction(function () use ($id, $data, $tenantId, $userId) {
|
|
|
|
|
$account = BankAccount::query()
|
|
|
|
|
->where('tenant_id', $tenantId)
|
|
|
|
|
->findOrFail($id);
|
|
|
|
|
|
|
|
|
|
$account->fill([
|
|
|
|
|
'bank_code' => $data['bank_code'] ?? $account->bank_code,
|
|
|
|
|
'bank_name' => $data['bank_name'] ?? $account->bank_name,
|
|
|
|
|
'account_number' => $data['account_number'] ?? $account->account_number,
|
|
|
|
|
'account_holder' => $data['account_holder'] ?? $account->account_holder,
|
|
|
|
|
'account_name' => $data['account_name'] ?? $account->account_name,
|
fix: [bank-account] 계좌 관리 API 누락 필드 8개 보강
- account_type, balance, currency, opened_at, branch_name, memo, sort_order, last_transaction_at 추가
- Model: fillable, casts, hidden, scopes, accessors를 MNG 모델 기준으로 통일
- Service: store/update에 누락 필드 반영
- FormRequest: Store/Update에 검증 규칙 추가
2026-02-20 23:20:49 +09:00
|
|
|
'account_type' => $data['account_type'] ?? $account->account_type,
|
|
|
|
|
'balance' => $data['balance'] ?? $account->balance,
|
|
|
|
|
'currency' => $data['currency'] ?? $account->currency,
|
|
|
|
|
'opened_at' => $data['opened_at'] ?? $account->opened_at,
|
|
|
|
|
'branch_name' => $data['branch_name'] ?? $account->branch_name,
|
|
|
|
|
'memo' => $data['memo'] ?? $account->memo,
|
2025-12-17 21:02:20 +09:00
|
|
|
'status' => $data['status'] ?? $account->status,
|
|
|
|
|
'assigned_user_id' => $data['assigned_user_id'] ?? $account->assigned_user_id,
|
fix: [bank-account] 계좌 관리 API 누락 필드 8개 보강
- account_type, balance, currency, opened_at, branch_name, memo, sort_order, last_transaction_at 추가
- Model: fillable, casts, hidden, scopes, accessors를 MNG 모델 기준으로 통일
- Service: store/update에 누락 필드 반영
- FormRequest: Store/Update에 검증 규칙 추가
2026-02-20 23:20:49 +09:00
|
|
|
'sort_order' => $data['sort_order'] ?? $account->sort_order,
|
2025-12-17 21:02:20 +09:00
|
|
|
'updated_by' => $userId,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$account->save();
|
|
|
|
|
|
|
|
|
|
return $account->fresh();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 계좌 삭제
|
|
|
|
|
*/
|
|
|
|
|
public function destroy(int $id): bool
|
|
|
|
|
{
|
|
|
|
|
$tenantId = $this->tenantId();
|
|
|
|
|
$userId = $this->apiUserId();
|
|
|
|
|
|
|
|
|
|
return DB::transaction(function () use ($id, $tenantId, $userId) {
|
|
|
|
|
$account = BankAccount::query()
|
|
|
|
|
->where('tenant_id', $tenantId)
|
|
|
|
|
->findOrFail($id);
|
|
|
|
|
|
|
|
|
|
// 대표계좌 삭제 시 다른 계좌를 대표로 설정
|
|
|
|
|
if ($account->is_primary) {
|
|
|
|
|
$nextPrimary = BankAccount::where('tenant_id', $tenantId)
|
|
|
|
|
->where('id', '!=', $id)
|
|
|
|
|
->where('status', 'active')
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
if ($nextPrimary) {
|
|
|
|
|
$nextPrimary->is_primary = true;
|
|
|
|
|
$nextPrimary->save();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$account->deleted_by = $userId;
|
|
|
|
|
$account->save();
|
|
|
|
|
$account->delete();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 계좌 상태 토글 (사용/정지)
|
|
|
|
|
*/
|
|
|
|
|
public function toggleStatus(int $id): BankAccount
|
|
|
|
|
{
|
|
|
|
|
$tenantId = $this->tenantId();
|
|
|
|
|
$userId = $this->apiUserId();
|
|
|
|
|
|
|
|
|
|
return DB::transaction(function () use ($id, $tenantId, $userId) {
|
|
|
|
|
$account = BankAccount::query()
|
|
|
|
|
->where('tenant_id', $tenantId)
|
|
|
|
|
->findOrFail($id);
|
|
|
|
|
|
|
|
|
|
$account->toggleStatus();
|
|
|
|
|
$account->updated_by = $userId;
|
|
|
|
|
$account->save();
|
|
|
|
|
|
|
|
|
|
return $account;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 대표계좌 설정
|
|
|
|
|
*/
|
|
|
|
|
public function setPrimary(int $id): BankAccount
|
|
|
|
|
{
|
|
|
|
|
$tenantId = $this->tenantId();
|
|
|
|
|
$userId = $this->apiUserId();
|
|
|
|
|
|
|
|
|
|
return DB::transaction(function () use ($id, $tenantId, $userId) {
|
|
|
|
|
// 기존 대표계좌 해제
|
|
|
|
|
BankAccount::where('tenant_id', $tenantId)
|
|
|
|
|
->where('is_primary', true)
|
|
|
|
|
->update(['is_primary' => false]);
|
|
|
|
|
|
|
|
|
|
// 새 대표계좌 설정
|
|
|
|
|
$account = BankAccount::query()
|
|
|
|
|
->where('tenant_id', $tenantId)
|
|
|
|
|
->findOrFail($id);
|
|
|
|
|
|
|
|
|
|
$account->is_primary = true;
|
|
|
|
|
$account->updated_by = $userId;
|
|
|
|
|
$account->save();
|
|
|
|
|
|
|
|
|
|
return $account;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 활성 계좌 목록 조회 (셀렉트박스용)
|
|
|
|
|
*/
|
|
|
|
|
public function getActiveAccounts(): array
|
|
|
|
|
{
|
|
|
|
|
$tenantId = $this->tenantId();
|
|
|
|
|
|
|
|
|
|
return BankAccount::query()
|
|
|
|
|
->where('tenant_id', $tenantId)
|
|
|
|
|
->where('status', 'active')
|
|
|
|
|
->orderByDesc('is_primary')
|
|
|
|
|
->orderBy('account_name')
|
|
|
|
|
->get(['id', 'account_name', 'bank_name', 'account_number', 'is_primary'])
|
|
|
|
|
->map(function ($account) {
|
|
|
|
|
return [
|
|
|
|
|
'id' => $account->id,
|
|
|
|
|
'account_name' => $account->account_name,
|
|
|
|
|
'bank_name' => $account->bank_name,
|
|
|
|
|
'display_number' => $account->getMaskedAccountNumber(),
|
|
|
|
|
'is_primary' => $account->is_primary,
|
|
|
|
|
];
|
|
|
|
|
})
|
|
|
|
|
->toArray();
|
|
|
|
|
}
|
|
|
|
|
}
|