fix: 거래처 연체/악성채권 저장 버그 수정
- ClientUpdateRequest, ClientStoreRequest에 is_overdue 필드 추가 - FormRequest rules에 누락되어 프론트엔드 값이 필터링됨 - ClientService.update()에 bad_debt 토글 연동 로직 추가 - bad_debt=true → BadDebt 레코드 생성 (status: collecting) - bad_debt=false → BadDebt 레코드 종료 (status: recovered) - ClientService의 has_bad_debt 판단 로직 수정 - 기존: sum(debt_amount) > 0 - 변경: exists() - 금액과 무관하게 레코드 존재 여부로 판단 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -96,6 +96,7 @@ public function rules(): array
|
||||
// 기타
|
||||
'memo' => 'nullable|string',
|
||||
'is_active' => 'nullable|boolean',
|
||||
'is_overdue' => 'nullable|boolean',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ public function rules(): array
|
||||
// 기타
|
||||
'memo' => 'nullable|string',
|
||||
'is_active' => 'nullable|boolean',
|
||||
'is_overdue' => 'nullable|boolean',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,13 +70,24 @@ public function index(array $params)
|
||||
->select('client_id', DB::raw('SUM(debt_amount) as total_bad_debt'))
|
||||
->pluck('total_bad_debt', 'client_id');
|
||||
|
||||
// 활성 악성채권이 있는 거래처 ID 목록 (금액과 무관하게 레코드 존재 여부)
|
||||
$clientsWithBadDebt = BadDebt::where('tenant_id', $tenantId)
|
||||
->whereIn('client_id', $clientIds)
|
||||
->whereIn('status', [BadDebt::STATUS_COLLECTING, BadDebt::STATUS_LEGAL_ACTION])
|
||||
->where('is_active', true)
|
||||
->whereNull('deleted_at')
|
||||
->distinct()
|
||||
->pluck('client_id')
|
||||
->flip()
|
||||
->toArray();
|
||||
|
||||
// 각 거래처에 미수금/악성채권 정보 추가
|
||||
$paginator->getCollection()->transform(function ($client) use ($salesByClient, $depositsByClient, $badDebtsByClient) {
|
||||
$paginator->getCollection()->transform(function ($client) use ($salesByClient, $depositsByClient, $badDebtsByClient, $clientsWithBadDebt) {
|
||||
$totalSales = $salesByClient[$client->id] ?? 0;
|
||||
$totalDeposits = $depositsByClient[$client->id] ?? 0;
|
||||
$client->outstanding_amount = max(0, $totalSales - $totalDeposits);
|
||||
$client->bad_debt_total = $badDebtsByClient[$client->id] ?? 0;
|
||||
$client->has_bad_debt = ($badDebtsByClient[$client->id] ?? 0) > 0;
|
||||
$client->has_bad_debt = isset($clientsWithBadDebt[$client->id]);
|
||||
|
||||
return $client;
|
||||
});
|
||||
@@ -108,15 +119,14 @@ public function show(int $id)
|
||||
$client->outstanding_amount = max(0, $totalSales - $totalDeposits);
|
||||
|
||||
// 악성채권 정보
|
||||
$badDebtTotal = BadDebt::where('tenant_id', $tenantId)
|
||||
$activeBadDebtQuery = BadDebt::where('tenant_id', $tenantId)
|
||||
->where('client_id', $id)
|
||||
->whereIn('status', [BadDebt::STATUS_COLLECTING, BadDebt::STATUS_LEGAL_ACTION])
|
||||
->where('is_active', true)
|
||||
->whereNull('deleted_at')
|
||||
->sum('debt_amount');
|
||||
->whereNull('deleted_at');
|
||||
|
||||
$client->bad_debt_total = $badDebtTotal;
|
||||
$client->has_bad_debt = $badDebtTotal > 0;
|
||||
$client->bad_debt_total = (clone $activeBadDebtQuery)->sum('debt_amount');
|
||||
$client->has_bad_debt = (clone $activeBadDebtQuery)->exists();
|
||||
|
||||
return $client;
|
||||
}
|
||||
@@ -173,6 +183,7 @@ private function generateClientCode(int $tenantId): string
|
||||
public function update(int $id, array $data)
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
$client = Client::where('tenant_id', $tenantId)->find($id);
|
||||
if (! $client) {
|
||||
@@ -182,11 +193,58 @@ public function update(int $id, array $data)
|
||||
// client_code 변경 불가 (프론트에서 보내도 무시)
|
||||
unset($data['client_code']);
|
||||
|
||||
// bad_debt 토글 처리 (bad_debts 테이블과 연동)
|
||||
$badDebtToggle = $data['bad_debt'] ?? null;
|
||||
unset($data['bad_debt']); // Client 모델에는 bad_debt 컬럼이 없으므로 제거
|
||||
|
||||
$client->update($data);
|
||||
|
||||
// bad_debt 토글이 명시적으로 전달된 경우에만 처리
|
||||
if ($badDebtToggle !== null) {
|
||||
$this->syncBadDebtStatus($client, (bool) $badDebtToggle, $userId);
|
||||
}
|
||||
|
||||
return $client->refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* 악성채권 상태 동기화
|
||||
* 프론트엔드의 bad_debt 토글에 따라 bad_debts 테이블 연동
|
||||
*/
|
||||
private function syncBadDebtStatus(Client $client, bool $hasBadDebt, ?int $userId): void
|
||||
{
|
||||
$tenantId = $client->tenant_id;
|
||||
|
||||
// 현재 활성 악성채권 조회
|
||||
$activeBadDebt = BadDebt::where('tenant_id', $tenantId)
|
||||
->where('client_id', $client->id)
|
||||
->where('is_active', true)
|
||||
->whereIn('status', [BadDebt::STATUS_COLLECTING, BadDebt::STATUS_LEGAL_ACTION])
|
||||
->first();
|
||||
|
||||
if ($hasBadDebt && ! $activeBadDebt) {
|
||||
// 악성채권 활성화: 새 레코드 생성
|
||||
BadDebt::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'client_id' => $client->id,
|
||||
'debt_amount' => $client->outstanding_balance ?? 0,
|
||||
'status' => BadDebt::STATUS_COLLECTING,
|
||||
'overdue_days' => 0,
|
||||
'occurred_at' => now(),
|
||||
'is_active' => true,
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
} elseif (! $hasBadDebt && $activeBadDebt) {
|
||||
// 악성채권 비활성화: 기존 레코드 종료 처리
|
||||
$activeBadDebt->update([
|
||||
'is_active' => false,
|
||||
'status' => BadDebt::STATUS_RECOVERED,
|
||||
'closed_at' => now(),
|
||||
'updated_by' => $userId,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/** 삭제 */
|
||||
public function destroy(int $id)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user