diff --git a/app/Http/Requests/Client/ClientStoreRequest.php b/app/Http/Requests/Client/ClientStoreRequest.php index b53e45b..09aa304 100644 --- a/app/Http/Requests/Client/ClientStoreRequest.php +++ b/app/Http/Requests/Client/ClientStoreRequest.php @@ -96,6 +96,7 @@ public function rules(): array // 기타 'memo' => 'nullable|string', 'is_active' => 'nullable|boolean', + 'is_overdue' => 'nullable|boolean', ]; } } diff --git a/app/Http/Requests/Client/ClientUpdateRequest.php b/app/Http/Requests/Client/ClientUpdateRequest.php index 5995bdb..98c90ed 100644 --- a/app/Http/Requests/Client/ClientUpdateRequest.php +++ b/app/Http/Requests/Client/ClientUpdateRequest.php @@ -96,6 +96,7 @@ public function rules(): array // 기타 'memo' => 'nullable|string', 'is_active' => 'nullable|boolean', + 'is_overdue' => 'nullable|boolean', ]; } } diff --git a/app/Services/ClientService.php b/app/Services/ClientService.php index e3f16da..6f44428 100644 --- a/app/Services/ClientService.php +++ b/app/Services/ClientService.php @@ -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) {