/** * 미수금 (Receivable) + 채권추심 (DebtCollection) 변환 */ import type { ReceivablesApiResponse, BadDebtApiResponse } from '../types'; import type { ReceivableData, DebtCollectionData, CheckPoint, CheckPointType, } from '@/components/business/CEODashboard/types'; import { formatAmount, normalizePath } from './common'; // ============================================ // 미수금 (Receivable) // ============================================ /** * 미수금 현황 CheckPoints 생성 */ function generateReceivableCheckPoints(api: ReceivablesApiResponse): CheckPoint[] { const checkPoints: CheckPoint[] = []; // 연체 거래처 경고 if (api.overdue_vendor_count > 0) { checkPoints.push({ id: 'rv-overdue', type: 'warning' as CheckPointType, message: `연체 거래처 ${api.overdue_vendor_count}곳. 회수 조치가 필요합니다.`, highlights: [ { text: `연체 거래처 ${api.overdue_vendor_count}곳`, color: 'red' as const }, ], }); } // 미수금 현황 if (api.total_receivables > 0) { checkPoints.push({ id: 'rv-total', type: 'info' as CheckPointType, message: `총 미수금 ${formatAmount(api.total_receivables)}입니다.`, highlights: [ { text: formatAmount(api.total_receivables), color: 'blue' as const }, ], }); } return checkPoints; } /** * Receivables API 응답 → Frontend 타입 변환 */ export function transformReceivableResponse(api: ReceivablesApiResponse): ReceivableData { // 누적 미수금 = 이월 + 매출 - 입금 const cumulativeReceivable = api.total_carry_forward + api.total_sales - api.total_deposits; return { cards: [ { id: 'rv1', label: '누적 미수금', amount: cumulativeReceivable, subItems: [ { label: '이월', value: api.total_carry_forward }, { label: '매출', value: api.total_sales }, { label: '입금', value: api.total_deposits }, ], }, { id: 'rv2', label: '당월 미수금', amount: api.total_receivables, subItems: [ { label: '매출', value: api.total_sales }, { label: '입금', value: api.total_deposits }, ], }, { id: 'rv3', label: '거래처 현황', amount: api.vendor_count, unit: '곳', subLabel: `연체 ${api.overdue_vendor_count}곳`, }, ], checkPoints: generateReceivableCheckPoints(api), //detailButtonLabel: '미수금 상세', detailButtonPath: normalizePath('/accounting/receivables-status'), }; } // ============================================ // 채권추심 (DebtCollection) // ============================================ /** * 채권추심 CheckPoints 생성 */ function generateDebtCollectionCheckPoints(api: BadDebtApiResponse): CheckPoint[] { const checkPoints: CheckPoint[] = []; // 법적조치 진행 중 if (api.legal_action_amount > 0) { checkPoints.push({ id: 'dc-legal', type: 'warning' as CheckPointType, message: `법적조치 진행 중 ${formatAmount(api.legal_action_amount)}입니다.`, highlights: [ { text: formatAmount(api.legal_action_amount), color: 'red' as const }, ], }); } // 회수 완료 if (api.recovered_amount > 0) { checkPoints.push({ id: 'dc-recovered', type: 'success' as CheckPointType, message: `총 ${formatAmount(api.recovered_amount)}을 회수 완료했습니다.`, highlights: [ { text: formatAmount(api.recovered_amount), color: 'green' as const }, { text: '회수 완료', color: 'green' as const }, ], }); } return checkPoints; } // TODO: 백엔드 per-card sub_label/count 제공 시 더미값 제거 // 채권추심 카드별 더미 서브라벨 (회사명 + 건수) const DEBT_COLLECTION_FALLBACK_SUB_LABELS: Record = { dc1: { company: '(주)부산화학 외', count: 5 }, dc2: { company: '(주)삼성테크 외', count: 3 }, dc3: { company: '(주)대한전자 외', count: 2 }, dc4: { company: '(주)한국정밀 외', count: 3 }, }; /** * 채권추심 subLabel 생성 헬퍼 * dc1(누적)은 API client_count 사용, 나머지는 더미값 */ function buildDebtSubLabel(cardId: string, clientCount?: number): string | undefined { const fallback = DEBT_COLLECTION_FALLBACK_SUB_LABELS[cardId]; if (!fallback) return undefined; const count = cardId === 'dc1' && clientCount !== undefined ? clientCount : fallback.count; if (count <= 0) return undefined; const remaining = count - 1; if (remaining > 0) { return `${fallback.company} ${remaining}건`; } return fallback.company.replace(/ 외$/, ''); } /** * BadDebt API 응답 → Frontend 타입 변환 */ export function transformDebtCollectionResponse(api: BadDebtApiResponse): DebtCollectionData { return { cards: [ { id: 'dc1', label: '누적 악성채권', amount: api.total_amount, subLabel: buildDebtSubLabel('dc1', api.client_count), }, { id: 'dc2', label: '추심중', amount: api.collecting_amount, subLabel: buildDebtSubLabel('dc2'), }, { id: 'dc3', label: '법적조치', amount: api.legal_action_amount, subLabel: buildDebtSubLabel('dc3'), }, { id: 'dc4', label: '회수완료', amount: api.recovered_amount, subLabel: buildDebtSubLabel('dc4'), }, ], checkPoints: generateDebtCollectionCheckPoints(api), detailButtonPath: normalizePath('/accounting/bad-debt-collection'), }; }