diff --git a/static/app.js b/static/app.js index 228fff7..eb83017 100644 --- a/static/app.js +++ b/static/app.js @@ -14,6 +14,57 @@ let currentLotAllocation = { // 재고 계산 모드 (localStorage에 저장) let inventoryCalculationMode = localStorage.getItem('inventoryMode') || 'all'; +// ==================== 포맷팅 함수 ==================== + +// 전화번호 포맷팅 (010-1234-5678 형식) +function formatPhoneNumber(phone) { + if (!phone) return '-'; + + // 숫자만 추출 + const cleaned = phone.replace(/\D/g, ''); + + // 길이에 따라 다른 포맷 적용 + if (cleaned.length === 11) { + // 010-1234-5678 + return cleaned.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3'); + } else if (cleaned.length === 10) { + // 02-1234-5678 또는 031-123-4567 + if (cleaned.startsWith('02')) { + return cleaned.replace(/(\d{2})(\d{4})(\d{4})/, '$1-$2-$3'); + } else { + return cleaned.replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3'); + } + } else if (cleaned.length === 9) { + // 02-123-4567 + return cleaned.replace(/(\d{2})(\d{3})(\d{4})/, '$1-$2-$3'); + } + + return phone; // 포맷팅할 수 없는 경우 원본 반환 +} + +// 주민번호 포맷팅 (980520-1****** 형식) +function formatJuminNumber(jumin, masked = true) { + if (!jumin) return '-'; + + // 숫자만 추출 + const cleaned = jumin.replace(/\D/g, ''); + + if (cleaned.length >= 13) { + const front = cleaned.substring(0, 6); + const back = cleaned.substring(6, 13); + + if (masked) { + // 뒷자리 마스킹 + return `${front.replace(/(\d{2})(\d{2})(\d{2})/, '$1$2$3')}-${back.charAt(0)}******`; + } else { + // 전체 표시 (편집 시) + return `${front.replace(/(\d{2})(\d{2})(\d{2})/, '$1$2$3')}-${back}`; + } + } + + return jumin; // 포맷팅할 수 없는 경우 원본 반환 +} + $(document).ready(function() { // 페이지 네비게이션 $('.sidebar .nav-link').on('click', function(e) { @@ -70,23 +121,40 @@ $(document).ready(function() { // 대시보드 데이터 로드 function loadDashboard() { - // 환자 수 + // 환자 수 + 총 마일리지 $.get('/api/patients', function(response) { if (response.success) { $('#totalPatients').text(response.data.length); + const totalMileage = response.data.reduce((sum, p) => sum + (p.mileage_balance || 0), 0); + $('#totalMileage').text(totalMileage.toLocaleString()); } }); // 재고 현황 (저장된 모드 사용) loadInventorySummary(); - // 오늘 조제 수 및 최근 조제 내역 + // 오늘 조제 수, 이번달 매출/마진, 최근 조제 내역 $.get('/api/compounds', function(response) { if (response.success) { const today = new Date().toISOString().split('T')[0]; + const currentMonth = new Date().toISOString().slice(0, 7); const todayCompounds = response.data.filter(c => c.compound_date === today); $('#todayCompounds').text(todayCompounds.length); + // 이번달 매출/마진 계산 + const monthData = response.data.filter(c => + c.compound_date && c.compound_date.startsWith(currentMonth) && + ['PAID', 'PENDING_DELIVERY', 'DELIVERED', 'COMPLETED'].includes(c.status) + ); + const monthSales = monthData.reduce((sum, c) => sum + (c.actual_payment_amount || c.sell_price_total || 0), 0); + const monthCost = monthData.reduce((sum, c) => sum + (c.cost_total || 0), 0); + const monthProfit = monthSales - monthCost; + const profitRate = monthSales > 0 ? ((monthProfit / monthSales) * 100).toFixed(1) : 0; + + $('#monthSales').text(monthSales.toLocaleString()); + $('#monthProfit').text(monthProfit.toLocaleString()); + $('#profitRate').text(profitRate + '%'); + // 최근 조제 내역 (최근 5개) const tbody = $('#recentCompounds'); tbody.empty(); @@ -97,10 +165,16 @@ $(document).ready(function() { let statusBadge = ''; switch(compound.status) { case 'PREPARED': - statusBadge = '조제완료'; + statusBadge = '조제완료'; break; - case 'DISPENSED': - statusBadge = '출고완료'; + case 'PAID': + statusBadge = '결제완료'; + break; + case 'DELIVERED': + statusBadge = '배송완료'; + break; + case 'COMPLETED': + statusBadge = '판매완료'; break; case 'CANCELLED': statusBadge = '취소'; @@ -153,7 +227,7 @@ $(document).ready(function() { tbody.append(`