fix: [corporate-cards] 선결제 배분 로직 고도화 (한도초과 우선 차감)

- 1단계: 한도 초과 카드에 초과분만큼 우선 차감
- 2단계: 잔여 금액을 현재 사용액 비율로 배분
- 마지막 카드에 반올림 오차 보정
- 어떤 카드도 100% 초과하지 않도록 보장
This commit is contained in:
김보곤
2026-02-24 17:33:57 +09:00
parent 93198a6518
commit aa107ca762

View File

@@ -403,15 +403,71 @@ function CorporateCardsManagement() {
return summaryData.cardUsages[normalized] || 0;
};
// 선결제 차감된 카드별 사용금액 (비율 배분)
// 선결제 스마트 배분: 1단계 한도초과 우선 차감 → 2단계 잔여 비율 배분
const cardDeductions = (() => {
const totalRaw = summaryData?.billingUsage || 0;
const prepaid = summaryData?.prepaidAmount || 0;
if (totalRaw <= 0 || prepaid <= 0) return {};
const activeCards = cards
.filter(c => c.status === 'active')
.map(c => ({
key: c.cardNumber.replace(/-/g, ''),
raw: getRawCardUsage(c.cardNumber),
limit: c.cardType === 'credit' ? c.creditLimit : 0,
}))
.filter(c => c.raw > 0);
if (activeCards.length === 0) return {};
let remaining = Math.min(prepaid, totalRaw);
const result = {};
activeCards.forEach(c => result[c.key] = 0);
// 1단계: 한도 초과 카드에 초과분만큼 우선 차감
for (const card of activeCards) {
if (remaining <= 0) break;
if (card.limit > 0 && card.raw > card.limit) {
const excess = card.raw - card.limit;
const deduct = Math.min(excess, remaining);
result[card.key] += deduct;
remaining -= deduct;
}
}
// 2단계: 잔여 금액을 현재 사용액 비율로 배분
if (remaining > 0) {
const withCurrent = activeCards
.map(c => ({ ...c, current: c.raw - result[c.key] }))
.filter(c => c.current > 0);
const totalCurrent = withCurrent.reduce((sum, c) => sum + c.current, 0);
if (totalCurrent > 0) {
let distributed = 0;
for (let i = 0; i < withCurrent.length; i++) {
const card = withCurrent[i];
let deduct;
if (i === withCurrent.length - 1) {
deduct = remaining - distributed;
} else {
deduct = Math.round(remaining * (card.current / totalCurrent));
}
deduct = Math.min(deduct, card.current);
result[card.key] += deduct;
distributed += deduct;
}
}
}
return result;
})();
// 선결제 차감된 카드별 사용금액
const getCardBillingUsage = (cardNumber) => {
const raw = getRawCardUsage(cardNumber);
if (!raw || !summaryData) return 0;
const totalRaw = summaryData.billingUsage || 0;
const prepaid = summaryData.prepaidAmount || 0;
if (totalRaw <= 0 || prepaid <= 0) return raw;
const ratio = raw / totalRaw;
return Math.max(0, Math.round(raw - prepaid * ratio));
if (!raw) return 0;
const normalized = cardNumber.replace(/-/g, '');
return Math.max(0, raw - (cardDeductions[normalized] || 0));
};
// 선결제 차감 후 실제 사용금액