From 725f14c59ab0fcd3358db9ba29b95e1bbeb4f615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=9C=EA=B3=A8=EC=95=BD=EC=82=AC?= Date: Thu, 19 Feb 2026 14:50:21 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=A1=B0=EC=A0=9C=20=EC=9B=90=EA=B0=80?= =?UTF-8?q?=20=EB=AF=B8=EB=A6=AC=EB=B3=B4=EA=B8=B0=20=EB=B0=8F=20=EC=9E=AC?= =?UTF-8?q?=EA=B3=A0=20=EC=83=81=ED=83=9C=20=ED=91=9C=EC=8B=9C=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 원가 미리보기: - 조제 실행 전 약재별 예상 원가(용량×단가) 및 합계 표시 - 용량/원산지/로트 변경 시 실시간 갱신 - 추가 약재의 이름 표시 오류 수정 (select 내 전체 옵션 텍스트 → 선택값만) 원산지 자동 선택: - 처방 로드 시 재고 충분한 최저가 원산지를 자동 선택 - "자동 선택" 상태가 아닌 실제 원산지가 선택되어 원가 즉시 계산 재고 상태 표시: - checkStockForCompound() TODO 제거, 실제 API 호출로 재고 확인 - 기존 원산지 선택을 덮어쓰지 않고 재고 상태만 갱신 - 선택 가능한 원산지가 2개 이상이면 "N종" 뱃지 표시 조제 폼 초기화: - 새 조제 시 제수 기본값(1)으로 총 첩수(20)/파우치(30) 자동 설정 - 처방 선택 시 총 첩수가 비어있으면 자동 계산 Co-Authored-By: Claude Opus 4.6 --- static/app.js | 137 +++++++++++++++++++++++++++++++++++++++++-- templates/index.html | 18 ++++++ 2 files changed, 149 insertions(+), 6 deletions(-) diff --git a/static/app.js b/static/app.js index e77b38b..bd1ca0c 100644 --- a/static/app.js +++ b/static/app.js @@ -1207,10 +1207,16 @@ $(document).ready(function() { $('#compoundForm').show(); $('#compoundEntryForm')[0].reset(); $('#compoundIngredients').empty(); + $('#costPreview').hide(); + // 제수 기본값(1)으로 첩수/파우치 초기화 + $('#jeCount').val(1); + $('#cheopTotal').val(20); + $('#pouchTotal').val(30); }); $('#cancelCompoundBtn').on('click', function() { $('#compoundForm').hide(); + $('#costPreview').hide(); }); // 제수 변경 시 첩수 자동 계산 @@ -1230,12 +1236,20 @@ $(document).ready(function() { $('#compoundFormula').on('change', function() { const formulaId = $(this).val(); + // 제수 기반 첩수/파우치 자동 계산 (초기값 반영) + const jeCount = parseFloat($('#jeCount').val()) || 0; + if (jeCount > 0 && !$('#cheopTotal').val()) { + $('#cheopTotal').val(jeCount * 20); + $('#pouchTotal').val(jeCount * 30); + } + // 원래 처방 구성 초기화 originalFormulaIngredients = {}; $('#customPrescriptionBadge').remove(); // 커스텀 뱃지 제거 if (!formulaId) { $('#compoundIngredients').empty(); + $('#costPreview').hide(); return; } @@ -1364,6 +1378,7 @@ $(document).ready(function() { }); // 약재별 총 용량 업데이트 + let _stockCheckTimer = null; function updateIngredientTotals() { const cheopTotal = parseFloat($('#cheopTotal').val()) || 0; @@ -1373,9 +1388,88 @@ $(document).ready(function() { $(this).find('.total-grams').text(totalGrams.toFixed(1)); }); - checkStockForCompound(); // 커스텀 처방 감지 호출 checkCustomPrescription(); + // 원가 미리보기 갱신 (즉시) + updateCostPreview(); + // 재고 상태 갱신 (디바운스 300ms) + clearTimeout(_stockCheckTimer); + _stockCheckTimer = setTimeout(() => checkStockForCompound(), 300); + } + + // 원가 미리보기 계산 + function updateCostPreview() { + const rows = $('#compoundIngredients tr'); + if (rows.length === 0) { + $('#costPreview').hide(); + return; + } + + const items = []; + let totalCost = 0; + let allHavePrice = true; + + rows.each(function() { + // 약재명: 처방에서 로드된 행은 텍스트, 추가된 행은 select의 선택값 + const firstTd = $(this).find('td:first'); + const herbSelect = firstTd.find('.herb-select-compound'); + const herbName = herbSelect.length > 0 + ? (herbSelect.find('option:selected').text().trim() || '미선택') + : firstTd.text().trim().split('(')[0].trim(); + const totalGrams = parseFloat($(this).find('.total-grams').text()) || 0; + const originSelect = $(this).find('.origin-select'); + const selectedOption = originSelect.find('option:selected'); + const unitPrice = parseFloat(selectedOption.attr('data-price')) || 0; + + // 수동 배분인 경우 data-lot-assignments에서 계산 + const lotAssignmentsStr = $(this).attr('data-lot-assignments'); + let itemCost = 0; + + if (lotAssignmentsStr) { + try { + const assignments = JSON.parse(lotAssignmentsStr); + assignments.forEach(a => { + itemCost += (a.quantity || 0) * (a.unit_price || 0); + }); + } catch(e) { + itemCost = totalGrams * unitPrice; + } + } else if (unitPrice > 0) { + itemCost = totalGrams * unitPrice; + } else { + allHavePrice = false; + } + + totalCost += itemCost; + items.push({ name: herbName, grams: totalGrams, unitPrice, cost: itemCost }); + }); + + // UI 렌더링 + const tbody = $('#costPreviewItems'); + tbody.empty(); + items.forEach(item => { + const costText = item.cost > 0 + ? formatCurrency(Math.round(item.cost)) + : '-'; + const priceText = item.unitPrice > 0 + ? `${item.grams.toFixed(1)}g × ₩${item.unitPrice.toFixed(1)}` + : `${item.grams.toFixed(1)}g`; + tbody.append(` + + ${item.name} ${priceText} + ${costText} + + `); + }); + + $('#costPreviewTotal').text(formatCurrency(Math.round(totalCost))); + + const status = allHavePrice && items.length > 0 + ? '확정' + : '일부 미확정'; + $('#costPreviewStatus').html(status); + + $('#costPreview').show(); } // 커스텀 처방 감지 함수 @@ -1452,13 +1546,30 @@ $(document).ready(function() { // 재고 확인 function checkStockForCompound() { + // 각 약재의 재고 상태를 API로 갱신 (기존 선택 보존) $('#compoundIngredients tr').each(function() { - const herbId = $(this).data('herb-id'); + const herbId = $(this).attr('data-herb-id'); + if (!herbId) return; const totalGrams = parseFloat($(this).find('.total-grams').text()) || 0; + const currentSelection = $(this).find('.origin-select').val(); const $stockStatus = $(this).find('.stock-status'); - // TODO: API 호출로 실제 재고 확인 - $stockStatus.text('재고 확인 필요'); + if (totalGrams > 0) { + $.get(`/api/herbs/${herbId}/available-lots`, function(response) { + if (response.success) { + const totalAvailable = response.data.total_quantity; + const origins = response.data.origins; + const altCount = origins.length; + const altBadge = altCount > 1 ? ` ${altCount}종` : ''; + + if (totalAvailable >= totalGrams) { + $stockStatus.html(`충분 (${totalAvailable.toFixed(1)}g)${altBadge}`); + } else { + $stockStatus.html(`부족 (${totalAvailable.toFixed(1)}g)${altBadge}`); + } + } + }); + } }); } @@ -3055,6 +3166,14 @@ $(document).ready(function() { selectElement.prop('disabled', false); + // 재고 충분한 첫 번째 원산지 자동 선택 (원가 미리보기용) + const firstAvailable = origins.find(o => o.total_quantity >= requiredQty); + if (firstAvailable) { + selectElement.val(firstAvailable.origin_country); + } else if (origins.length > 0) { + selectElement.val(origins[0].origin_country); + } + // 원산지 선택 변경 이벤트 (수동 배분 모달 트리거) selectElement.off('change').on('change', function() { const selectedValue = $(this).val(); @@ -3072,18 +3191,24 @@ $(document).ready(function() { // 기존 자동/원산지 선택 - lot_assignments 제거 row.removeAttr('data-lot-assignments'); } + updateCostPreview(); }); // 재고 상태 업데이트 const totalAvailable = response.data.total_quantity; const statusElement = $(`tr[data-herb-id="${herbId}"] .stock-status`); + const altCount = origins.length; + const altBadge = altCount > 1 ? ` ${altCount}종` : ''; if (totalAvailable >= requiredQty) { - statusElement.html(`충분 (${totalAvailable.toFixed(1)}g)`); + statusElement.html(`충분 (${totalAvailable.toFixed(1)}g)${altBadge}`); } else { - statusElement.html(`부족 (${totalAvailable.toFixed(1)}g)`); + statusElement.html(`부족 (${totalAvailable.toFixed(1)}g)${altBadge}`); } } + + // 원가 미리보기 갱신 + updateCostPreview(); } }); } diff --git a/templates/index.html b/templates/index.html index a60ea5b..585f949 100644 --- a/templates/index.html +++ b/templates/index.html @@ -486,6 +486,24 @@ 약재 추가 + +