feat: 도매상 잔고 모달에 월간 매출 추가
- 백제/지오영/수인 월간매출 API 라우트 추가 - 모달 UI: 잔고 + 월간 매출 동시 표시 - 총 주문액 / 총 미수금 요약 표시
This commit is contained in:
@@ -2256,26 +2256,43 @@
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
.balance-total {
|
||||
.balance-summary {
|
||||
background: linear-gradient(135deg, rgba(168, 85, 247, 0.1), rgba(6, 182, 212, 0.1));
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
margin-top: 12px;
|
||||
gap: 16px;
|
||||
}
|
||||
.balance-total-label {
|
||||
.summary-item {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
}
|
||||
.summary-label {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.balance-total-value {
|
||||
font-size: 28px;
|
||||
.summary-value {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
}
|
||||
.summary-value.sales {
|
||||
color: #10b981;
|
||||
}
|
||||
.summary-value.balance {
|
||||
background: linear-gradient(135deg, #a855f7, #06b6d4);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.summary-divider {
|
||||
width: 1px;
|
||||
height: 50px;
|
||||
background: var(--border);
|
||||
}
|
||||
.balance-updated {
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
@@ -2300,17 +2317,23 @@
|
||||
baekje: {
|
||||
id: 'baekje', name: '백제약품', icon: '💉',
|
||||
logo: '/static/img/logo_baekje.svg',
|
||||
color: '#f59e0b', api: '/api/baekje/balance'
|
||||
color: '#f59e0b',
|
||||
balanceApi: '/api/baekje/balance',
|
||||
salesApi: '/api/baekje/monthly-sales'
|
||||
},
|
||||
geoyoung: {
|
||||
id: 'geoyoung', name: '지오영', icon: '🏭',
|
||||
logo: '/static/img/logo_geoyoung.ico',
|
||||
color: '#06b6d4', api: '/api/geoyoung/balance'
|
||||
color: '#06b6d4',
|
||||
balanceApi: '/api/geoyoung/balance',
|
||||
salesApi: '/api/geoyoung/monthly-sales'
|
||||
},
|
||||
sooin: {
|
||||
id: 'sooin', name: '수인약품', icon: '💊',
|
||||
logo: '/static/img/logo_sooin.svg',
|
||||
color: '#a855f7', api: '/api/sooin/balance'
|
||||
color: '#a855f7',
|
||||
balanceApi: '/api/sooin/balance',
|
||||
salesApi: '/api/sooin/monthly-sales'
|
||||
}
|
||||
};
|
||||
const WHOLESALER_ORDER = ['baekje', 'geoyoung', 'sooin'];
|
||||
@@ -2320,36 +2343,47 @@
|
||||
content.innerHTML = `
|
||||
<div class="loading-state">
|
||||
<div class="loading-spinner"></div>
|
||||
<div>잔고 조회 중...</div>
|
||||
<div>잔고 및 매출 조회 중...</div>
|
||||
</div>`;
|
||||
|
||||
const wholesalers = WHOLESALER_ORDER.map(id => WHOLESALER_CONFIG[id]);
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = now.getMonth() + 1;
|
||||
|
||||
const results = {};
|
||||
const balanceResults = {};
|
||||
const salesResults = {};
|
||||
let totalBalance = 0;
|
||||
let totalSales = 0;
|
||||
|
||||
// 병렬로 조회
|
||||
await Promise.all(wholesalers.map(async (ws) => {
|
||||
try {
|
||||
const resp = await fetch(ws.api, { timeout: 30000 });
|
||||
const data = await resp.json();
|
||||
results[ws.id] = data;
|
||||
if (data.success && data.balance) {
|
||||
totalBalance += data.balance;
|
||||
}
|
||||
} catch (err) {
|
||||
results[ws.id] = { success: false, error: err.message };
|
||||
}
|
||||
}));
|
||||
// 병렬로 잔고 + 월간매출 조회
|
||||
await Promise.all(wholesalers.flatMap(ws => [
|
||||
// 잔고 조회
|
||||
fetch(ws.balanceApi).then(r => r.json()).then(data => {
|
||||
balanceResults[ws.id] = data;
|
||||
if (data.success && data.balance) totalBalance += data.balance;
|
||||
}).catch(err => {
|
||||
balanceResults[ws.id] = { success: false, error: err.message };
|
||||
}),
|
||||
// 월간 매출 조회
|
||||
fetch(`${ws.salesApi}?year=${year}&month=${month}`).then(r => r.json()).then(data => {
|
||||
salesResults[ws.id] = data;
|
||||
if (data.success && data.total_amount) totalSales += data.total_amount;
|
||||
}).catch(err => {
|
||||
salesResults[ws.id] = { success: false, error: err.message };
|
||||
})
|
||||
]));
|
||||
|
||||
// 결과 렌더링
|
||||
let html = '<div class="balance-grid">';
|
||||
|
||||
wholesalers.forEach(ws => {
|
||||
const data = results[ws.id];
|
||||
const isError = !data.success;
|
||||
const balance = data.balance || 0;
|
||||
const prevBalance = data.prev_balance || data.prev_month_balance || 0;
|
||||
const balData = balanceResults[ws.id] || {};
|
||||
const salesData = salesResults[ws.id] || {};
|
||||
const isError = !balData.success;
|
||||
const balance = balData.balance || 0;
|
||||
const monthlySales = salesData.total_amount || 0;
|
||||
const monthlyPaid = salesData.total_paid || 0;
|
||||
|
||||
html += `
|
||||
<div class="balance-card ${isError ? 'error' : ''}">
|
||||
@@ -2362,8 +2396,9 @@
|
||||
<div class="balance-name">${ws.name}</div>
|
||||
<div class="balance-detail">
|
||||
${isError
|
||||
? `❌ ${data.error || '조회 실패'}`
|
||||
: `전월/전일: ${prevBalance.toLocaleString()}원`}
|
||||
? `❌ ${balData.error || '조회 실패'}`
|
||||
: `${month}월 매출: <span style="color:${ws.color};font-weight:600;">${monthlySales.toLocaleString()}원</span>
|
||||
${monthlyPaid > 0 ? ` · 입금: ${monthlyPaid.toLocaleString()}원` : ''}`}
|
||||
</div>
|
||||
</div>
|
||||
<div class="balance-amount">
|
||||
@@ -2376,9 +2411,16 @@
|
||||
});
|
||||
|
||||
html += `
|
||||
<div class="balance-total">
|
||||
<div class="balance-total-label">총 미수금</div>
|
||||
<div class="balance-total-value">${totalBalance.toLocaleString()}원</div>
|
||||
<div class="balance-summary">
|
||||
<div class="summary-item">
|
||||
<div class="summary-label">📊 ${month}월 총 주문</div>
|
||||
<div class="summary-value sales">${totalSales.toLocaleString()}원</div>
|
||||
</div>
|
||||
<div class="summary-divider"></div>
|
||||
<div class="summary-item">
|
||||
<div class="summary-label">💰 총 미수금</div>
|
||||
<div class="summary-value balance">${totalBalance.toLocaleString()}원</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="balance-updated">🕐 ${new Date().toLocaleString('ko-KR')}</div>
|
||||
</div>`;
|
||||
|
||||
Reference in New Issue
Block a user