fix:카드별 사용금액 프로그래스바 바로빌 실거래 데이터 반영

- summary API에 cardUsages(카드번호별 사용금액) 응답 추가
- 카드 목록 프로그래스바가 바로빌 거래 합산 기준으로 표시
- 체크카드도 사용금액 있으면 금액 표시

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
김보곤
2026-02-11 10:31:06 +09:00
parent d78d431350
commit 5cb92c0fd7
2 changed files with 60 additions and 27 deletions

View File

@@ -228,8 +228,8 @@ public function summary(): JsonResponse
// 카드번호 목록
$cardNumbers = $activeCards->pluck('card_number')->toArray();
// 사용금액 계산
$billingUsage = $this->calculateBillingUsage(
// 사용금액 계산 (전체 + 카드별)
$usageResult = $this->calculateBillingUsage(
$tenantId,
$billingStart->format('Y-m-d'),
$billingEnd->format('Y-m-d'),
@@ -251,7 +251,8 @@ public function summary(): JsonResponse
'start' => $billingStart->format('Y-m-d'),
'end' => $billingEnd->format('Y-m-d'),
],
'billingUsage' => $billingUsage,
'billingUsage' => $usageResult['total'],
'cardUsages' => $usageResult['perCard'],
'prepaidAmount' => (int) $prepayment->amount,
'prepaidMemo' => $prepayment->memo ?? '',
],
@@ -328,13 +329,13 @@ private function getAdjustedPaymentDate(int $tenantId, int $year, int $month, in
/**
* 청구기간 사용금액 계산 (바로빌 카드거래 합산)
*/
private function calculateBillingUsage(int $tenantId, string $startDate, string $endDate, array $cardNumbers): int
private function calculateBillingUsage(int $tenantId, string $startDate, string $endDate, array $cardNumbers): array
{
// 카드번호 정규화 (하이픈 제거)
// 카드번호 정규화 (하이픈 제거) + 원본↔정규화 매핑
$normalizedNums = array_map(fn($num) => str_replace('-', '', $num), $cardNumbers);
if (empty($normalizedNums)) {
return 0;
return ['total' => 0, 'perCard' => []];
}
// use_date는 YYYYMMDD 형식
@@ -351,18 +352,24 @@ private function calculateBillingUsage(int $tenantId, string $startDate, string
->get();
$total = 0;
$perCard = [];
foreach ($transactions as $tx) {
if ($hiddenKeys->contains($tx->unique_key)) {
continue;
}
$amount = 0;
if ($tx->approval_type === '1') {
$total += (int) $tx->approval_amount; // 승인
$amount = (int) $tx->approval_amount;
} elseif ($tx->approval_type === '2') {
$total -= (int) $tx->approval_amount; // 취소
$amount = -(int) $tx->approval_amount;
}
$total += $amount;
$cardNum = $tx->card_num;
$perCard[$cardNum] = ($perCard[$cardNum] ?? 0) + $amount;
}
return $total;
return ['total' => $total, 'perCard' => (object) $perCard];
}
}

View File

@@ -397,6 +397,13 @@ function CorporateCardsManagement() {
return 'bg-emerald-500';
};
// 카드번호로 바로빌 사용금액 조회 (하이픈 제거 매칭)
const getCardBillingUsage = (cardNumber) => {
if (!summaryData?.cardUsages || !cardNumber) return 0;
const normalized = cardNumber.replace(/-/g, '');
return summaryData.cardUsages[normalized] || 0;
};
// 총 한도 및 사용액
const totalLimit = cards.filter(c => c.status === 'active' && c.cardType === 'credit').reduce((sum, c) => sum + c.creditLimit, 0);
const totalUsage = cards.filter(c => c.status === 'active').reduce((sum, c) => sum + c.currentUsage, 0);
@@ -602,24 +609,43 @@ className={`grid grid-cols-12 gap-4 px-4 py-3 border-b border-gray-100 cursor-po
{/* 사용현황 */}
<div className="col-span-3 flex items-center gap-2">
{card.cardType === 'debit' ? (
<span className="px-2 py-0.5 bg-blue-100 text-blue-700 text-xs font-medium rounded">체크카드</span>
) : card.creditLimit > 0 ? (
<div className="flex-1">
<div className="flex items-center justify-between text-xs mb-1">
<span className="text-gray-500">{formatCurrency(card.currentUsage)}</span>
<span className="text-gray-400">{getUsagePercent(card.currentUsage, card.creditLimit)}%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-1.5">
<div
className={`h-1.5 rounded-full ${getUsageColor(getUsagePercent(card.currentUsage, card.creditLimit))}`}
style={{ width: `${Math.min(getUsagePercent(card.currentUsage, card.creditLimit), 100)}%` }}
></div>
</div>
</div>
) : (
<span className="px-2 py-0.5 bg-violet-100 text-violet-700 text-xs font-medium rounded">신용카드</span>
)}
{(() => {
const billingUsage = getCardBillingUsage(card.cardNumber);
if (card.cardType === 'debit') {
return billingUsage > 0 ? (
<div className="flex-1">
<div className="flex items-center justify-between text-xs">
<span className="text-gray-500">{formatCurrency(billingUsage)}</span>
<span className="px-1.5 py-0.5 bg-blue-100 text-blue-700 text-xs font-medium rounded">체크</span>
</div>
</div>
) : (
<span className="px-2 py-0.5 bg-blue-100 text-blue-700 text-xs font-medium rounded">체크카드</span>
);
}
if (card.creditLimit > 0) {
const pct = getUsagePercent(billingUsage, card.creditLimit);
return (
<div className="flex-1">
<div className="flex items-center justify-between text-xs mb-1">
<span className="text-gray-500">{formatCurrency(billingUsage)}</span>
<span className="text-gray-400">{pct}%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-1.5">
<div
className={`h-1.5 rounded-full ${getUsageColor(pct)}`}
style={{ width: `${Math.min(pct, 100)}%` }}
></div>
</div>
</div>
);
}
return billingUsage > 0 ? (
<span className="text-xs text-gray-500">{formatCurrency(billingUsage)}</span>
) : (
<span className="px-2 py-0.5 bg-violet-100 text-violet-700 text-xs font-medium rounded">신용카드</span>
);
})()}
</div>
{/* 상태 */}