Files
sam-react-prod/src/lib/api/dashboard/transformers/expense.ts
유병철 00a6209347 feat: 레이아웃/출하/생산/회계/대시보드 전반 개선
- HeaderFavoritesBar 대폭 개선
- Sidebar/AuthenticatedLayout 소폭 수정
- ShipmentCreate, VehicleDispatch 출하 관련 개선
- WorkOrderCreate/Edit, WorkerScreen 생산 관련 개선
- InspectionCreate 자재 입고검사 개선
- DailyReport, VendorDetail 회계 수정
- CEO 대시보드: CardManagement/DailyProduction/DailyAttendance 섹션 개선
- useCEODashboard, expense transformer 정비
- DocumentViewer, PDF generate route 소폭 수정
- bill-prototype 개발 페이지 추가
- mockData 불필요 데이터 제거
2026-03-05 13:35:48 +09:00

240 lines
8.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 월 예상 지출 (MonthlyExpense) + 카드/가지급금 (CardManagement) 변환
*/
import type {
ExpectedExpenseApiResponse,
CardTransactionApiResponse,
LoanDashboardApiResponse,
TaxSimulationApiResponse,
} from '../types';
import type {
MonthlyExpenseData,
CardManagementData,
CheckPoint,
CheckPointType,
} from '@/components/business/CEODashboard/types';
import { formatAmount } from './common';
// ============================================
// 월 예상 지출 (MonthlyExpense)
// ============================================
/**
* 당월 예상 지출 CheckPoints 생성
*/
function generateMonthlyExpenseCheckPoints(api: ExpectedExpenseApiResponse): CheckPoint[] {
const checkPoints: CheckPoint[] = [];
// 총 예상 지출
checkPoints.push({
id: 'me-total',
type: 'info' as CheckPointType,
message: `이번 달 예상 지출은 ${formatAmount(api.total_amount)}입니다.`,
highlights: [
{ text: formatAmount(api.total_amount), color: 'blue' as const },
],
});
return checkPoints;
}
/**
* ExpectedExpense API 응답 → Frontend 타입 변환
* 주의: 실제 API는 상세 분류(매입/카드/어음 등)를 제공하지 않음
* by_transaction_type에서 추출하거나 기본값 사용
*/
export function transformMonthlyExpenseResponse(api: ExpectedExpenseApiResponse): MonthlyExpenseData {
// transaction_type별 금액 추출
const purchaseTotal = api.by_transaction_type['purchase']?.total ?? 0;
const cardTotal = api.by_transaction_type['card']?.total ?? 0;
const billTotal = api.by_transaction_type['bill']?.total ?? 0;
return {
cards: [
{
id: 'me1',
label: '매입',
amount: purchaseTotal,
},
{
id: 'me2',
label: '카드',
amount: cardTotal,
},
{
id: 'me3',
label: '발행어음',
amount: billTotal,
},
{
id: 'me4',
label: '총 예상 지출 합계',
amount: api.total_amount,
},
],
checkPoints: generateMonthlyExpenseCheckPoints(api),
};
}
// ============================================
// 카드/가지급금 (CardManagement) — D1.7 5장 카드 구조
// ============================================
/**
* 카드/가지급금 CheckPoints 생성 (D1.7)
*
* CP1: 법인카드 → 가지급금 전환 경고
* CP2: 인정이자 발생 현황
* CP3: 접대비 불인정 항목 감지
* CP4: 주말 카드 사용 감지 (향후 확장)
*/
function generateCardManagementCheckPoints(
loanApi?: LoanDashboardApiResponse | null,
taxApi?: TaxSimulationApiResponse | null,
cardApi?: CardTransactionApiResponse | null,
): CheckPoint[] {
const checkPoints: CheckPoint[] = [];
const totalOutstanding = loanApi?.summary?.total_outstanding ?? 0;
const interestRate = taxApi?.loan_summary?.interest_rate ?? 4.6;
const recognizedInterest = taxApi?.loan_summary?.recognized_interest ?? 0;
// CP1: 법인카드 사용 중 가지급금 전환 경고
if (totalOutstanding > 0) {
checkPoints.push({
id: 'cm-cp1',
type: 'success' as CheckPointType,
message: `법인카드 사용 중 ${formatAmount(totalOutstanding)}이 가지급금으로 전환되었습니다. 연 ${interestRate}% 인정이자가 발생합니다.`,
highlights: [
{ text: formatAmount(totalOutstanding), color: 'red' as const },
{ text: '가지급금', color: 'red' as const },
{ text: `${interestRate}% 인정이자`, color: 'red' as const },
],
});
}
// CP2: 인정이자 발생 현황
if (totalOutstanding > 0 && recognizedInterest > 0) {
checkPoints.push({
id: 'cm-cp2',
type: 'success' as CheckPointType,
message: `현재 가지급금 ${formatAmount(totalOutstanding)} × ${interestRate}% = 연간 약 ${formatAmount(recognizedInterest)}의 인정이자가 발생 중입니다.`,
highlights: [
{ text: `연간 약 ${formatAmount(recognizedInterest)}의 인정이자`, color: 'red' as const },
],
});
}
// CP3: 접대비 불인정 항목 감지
const entertainmentCount = loanApi?.category_breakdown?.entertainment?.unverified_count ?? 0;
if (entertainmentCount > 0) {
checkPoints.push({
id: 'cm-cp3',
type: 'success' as CheckPointType,
message: '상품권/귀금속 등 접대비 불인정 항목 결제 감지. 가지급금 처리 예정입니다.',
highlights: [
{ text: '불인정 항목 결제 감지', color: 'red' as const },
],
});
}
// CP4: 주말 카드 사용 감지 (향후 card-transactions API 확장 시)
// 현재는 cardApi 데이터에서 주말 사용 정보가 없으므로 placeholder
if (cardApi && cardApi.current_month_total > 0) {
// 향후 weekend_amount 필드 추가 시 활성화
}
return checkPoints;
}
/**
* CardTransaction API 응답 → Frontend 타입 변환
* D1.7 5장 카드 구조:
* - cm1: 카드 (loans category_breakdown.card)
* - cm2: 경조사 (loans category_breakdown.congratulatory)
* - cm3: 상품권 (loans category_breakdown.gift_certificate)
* - cm4: 접대비 (loans category_breakdown.entertainment)
* - cm_total: 총 가지급금 합계 (loans summary.total_outstanding)
*/
export function transformCardManagementResponse(
summaryApi: CardTransactionApiResponse,
loanApi?: LoanDashboardApiResponse | null,
taxApi?: TaxSimulationApiResponse | null,
fallbackData?: CardManagementData
): CardManagementData {
const breakdown = loanApi?.category_breakdown;
const totalOutstanding = loanApi?.summary?.total_outstanding ?? 0;
// 카테고리별 금액 추출
const cardAmount = breakdown?.card?.outstanding_amount ?? fallbackData?.cards[0]?.amount ?? 0;
const congratulatoryAmount = breakdown?.congratulatory?.outstanding_amount ?? fallbackData?.cards[1]?.amount ?? 0;
const giftCertificateAmount = breakdown?.gift_certificate?.outstanding_amount ?? fallbackData?.cards[2]?.amount ?? 0;
const entertainmentAmount = breakdown?.entertainment?.outstanding_amount ?? fallbackData?.cards[3]?.amount ?? 0;
// 카테고리별 미증빙/미정리 건수
const cardUnverified = breakdown?.card?.unverified_count ?? 0;
const congratulatoryUnverified = breakdown?.congratulatory?.unverified_count ?? 0;
const giftCertificateUnverified = breakdown?.gift_certificate?.unverified_count ?? 0;
const entertainmentUnverified = breakdown?.entertainment?.unverified_count ?? 0;
// 총 합계 (API summary 또는 카테고리 합산)
const totalAmount = totalOutstanding > 0
? totalOutstanding
: cardAmount + congratulatoryAmount + giftCertificateAmount + entertainmentAmount;
// 가지급금 경고 배너 표시 여부 (가지급금 잔액 > 0이면 표시)
const hasLoanWarning = totalAmount > 0;
return {
warningBanner: hasLoanWarning
? (fallbackData?.warningBanner ?? '가지급금 인정이자 4.6%, 법인세 및 연말정산 시 대표자 종합세 가중 주의')
: undefined,
cards: [
// cm1: 카드
{
id: 'cm1',
label: '카드',
amount: cardAmount,
subLabel: cardUnverified > 0 ? `미정리 ${cardUnverified}` : undefined,
subAmount: cardUnverified > 0 ? cardAmount : undefined,
isHighlighted: cardUnverified > 0,
},
// cm2: 경조사
{
id: 'cm2',
label: '경조사',
amount: congratulatoryAmount,
subLabel: congratulatoryUnverified > 0 ? `미증빙 ${congratulatoryUnverified}` : undefined,
subAmount: congratulatoryUnverified > 0 ? congratulatoryAmount : undefined,
isHighlighted: congratulatoryUnverified > 0,
},
// cm3: 상품권
{
id: 'cm3',
label: '상품권',
amount: giftCertificateAmount,
subLabel: giftCertificateUnverified > 0 ? `미증빙 ${giftCertificateUnverified}` : undefined,
subAmount: giftCertificateUnverified > 0 ? giftCertificateAmount : undefined,
isHighlighted: giftCertificateUnverified > 0,
},
// cm4: 접대비
{
id: 'cm4',
label: '접대비',
amount: entertainmentAmount,
subLabel: entertainmentUnverified > 0 ? `미증빙 ${entertainmentUnverified}` : undefined,
subAmount: entertainmentUnverified > 0 ? entertainmentAmount : undefined,
isHighlighted: entertainmentUnverified > 0,
},
// cm_total: 총 가지급금 합계
{
id: 'cm_total',
label: '총 가지급금 합계',
amount: totalAmount,
},
],
checkPoints: generateCardManagementCheckPoints(loanApi, taxApi, summaryApi),
};
}