fix: [corporate-cards] 선결제 배분 로직 고도화 (한도초과 우선 차감)
- 1단계: 한도 초과 카드에 초과분만큼 우선 차감 - 2단계: 잔여 금액을 현재 사용액 비율로 배분 - 마지막 카드에 반올림 오차 보정 - 어떤 카드도 100% 초과하지 않도록 보장
This commit is contained in:
@@ -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));
|
||||
};
|
||||
|
||||
// 선결제 차감 후 실제 사용금액
|
||||
|
||||
Reference in New Issue
Block a user