From e7096f7bedb822065987cef8e07cc19bc20076f1 Mon Sep 17 00:00:00 2001 From: thug0bin Date: Wed, 4 Mar 2026 14:28:41 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=A0=9C=ED=92=88=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=EC=97=90=20=EC=9C=84=EC=B9=98=20=EC=BB=AC=EB=9F=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CD_item_position.CD_NM_sale 조회 (person-lookup-web-local 참고) - 3개 쿼리 모두 LEFT JOIN CD_item_position 추가 - 위치 뱃지 스타일 (노란색 배경) --- backend/app.py | 13 +- backend/templates/admin_products.html | 290 ++++++++++++++------------ 2 files changed, 161 insertions(+), 142 deletions(-) diff --git a/backend/app.py b/backend/app.py index f9fe199..4c73bc4 100644 --- a/backend/app.py +++ b/backend/app.py @@ -3345,9 +3345,11 @@ def api_products(): ISNULL(G.SplName, '') as supplier, 0 as is_set, G.POS_BOON as pos_boon, - IT.IM_QT_sale_debit as stock + IT.IM_QT_sale_debit as stock, + ISNULL(POS.CD_NM_sale, '') as location FROM CD_GOODS G INNER JOIN IM_total IT ON G.DrugCode = IT.DrugCode AND IT.IM_QT_sale_debit > 0 + LEFT JOIN CD_item_position POS ON G.DrugCode = POS.DrugCode WHERE 1=1 {animal_condition} {search_condition} @@ -3367,9 +3369,11 @@ def api_products(): ISNULL(G.SplName, '') as supplier, 0 as is_set, G.POS_BOON as pos_boon, - ISNULL(IT.IM_QT_sale_debit, 0) as stock + ISNULL(IT.IM_QT_sale_debit, 0) as stock, + ISNULL(POS.CD_NM_sale, '') as location FROM CD_GOODS G LEFT JOIN IM_total IT ON G.DrugCode = IT.DrugCode + LEFT JOIN CD_item_position POS ON G.DrugCode = POS.DrugCode WHERE G.POS_BOON = '010103' {search_condition} ORDER BY G.GoodsName @@ -3389,9 +3393,11 @@ def api_products(): END as supplier, CASE WHEN SET_CHK.is_set = 1 THEN 1 ELSE 0 END as is_set, G.POS_BOON as pos_boon, - ISNULL(IT.IM_QT_sale_debit, 0) as stock + ISNULL(IT.IM_QT_sale_debit, 0) as stock, + ISNULL(POS.CD_NM_sale, '') as location FROM CD_GOODS G LEFT JOIN IM_total IT ON G.DrugCode = IT.DrugCode + LEFT JOIN CD_item_position POS ON G.DrugCode = POS.DrugCode OUTER APPLY ( SELECT TOP 1 CD_CD_BARCODE FROM CD_ITEM_UNIT_MEMBER @@ -3445,6 +3451,7 @@ def api_products(): 'is_set': bool(row.is_set), 'is_animal_drug': is_animal, 'stock': int(row.stock) if row.stock else 0, + 'location': row.location or '', # 위치 'apc': apc, 'category': None, # PostgreSQL에서 lazy fetch 'wholesaler_stock': None, diff --git a/backend/templates/admin_products.html b/backend/templates/admin_products.html index 1acf396..af16793 100644 --- a/backend/templates/admin_products.html +++ b/backend/templates/admin_products.html @@ -1,9 +1,9 @@ - + - 제품 검색 - 청춘약국 + ?�품 검??- �?��?�국 @@ -16,7 +16,7 @@ color: #1e293b; } - /* ── 헤더 ── */ + /* ?�?� ?�더 ?�?� */ .header { background: linear-gradient(135deg, #7c3aed 0%, #8b5cf6 50%, #a78bfa 100%); padding: 28px 32px 24px; @@ -46,14 +46,14 @@ opacity: 0.85; } - /* ── 컨텐츠 ── */ + /* ?�?� 컨텐�??�?� */ .content { max-width: 1100px; margin: 0 auto; padding: 24px 20px 60px; } - /* ── 플로팅 챗봇 ── */ + /* ?�?� ?�로??챗봇 ?�?� */ .chatbot-panel { position: fixed; right: 24px; @@ -224,7 +224,7 @@ 30% { transform: translateY(-6px); opacity: 1; } } - /* ── 챗봇 토글 버튼 (항상 표시) ── */ + /* ?�?� 챗봇 ?��? 버튼 (??�� ?�시) ?�?� */ .chatbot-toggle { position: fixed; bottom: 24px; @@ -253,7 +253,7 @@ box-shadow: 0 4px 20px rgba(239, 68, 68, 0.4); } - /* 모바일 */ + /* 모바??*/ @media (max-width: 640px) { .chatbot-panel { right: 0; @@ -266,7 +266,7 @@ .chatbot-toggle { bottom: 16px; right: 16px; } } - /* ── 검색 영역 ── */ + /* ?�?� 검???�역 ?�?� */ .search-section { background: #fff; border-radius: 14px; @@ -320,7 +320,7 @@ margin-right: 8px; } - /* ── 결과 카운트 ── */ + /* ?�?� 결과 카운???�?� */ .result-count { margin-bottom: 16px; font-size: 14px; @@ -331,7 +331,7 @@ font-weight: 700; } - /* ── 테이블 ── */ + /* ?�?� ?�이�??�?� */ .table-wrap { background: #fff; border-radius: 14px; @@ -362,7 +362,7 @@ tbody tr:hover { background: #faf5ff; } tbody tr:last-child td { border-bottom: none; } - /* ── 상품 정보 ── */ + /* ?�?� ?�품 ?�보 ?�?� */ .product-name { font-weight: 600; color: #1e293b; @@ -377,7 +377,7 @@ font-weight: 500; } - /* ── 코드/바코드 ── */ + /* ?�?� 코드/바코???�?� */ .code { font-family: 'JetBrains Mono', monospace; font-size: 12px; @@ -397,14 +397,23 @@ background: #f1f5f9; color: #94a3b8; } + .location-badge { + display: inline-block; + background: #fef3c7; + color: #92400e; + font-size: 11px; + padding: 3px 8px; + border-radius: 4px; + font-weight: 500; + } - /* ── 가격 ── */ + /* ?�?� 가�??�?� */ .price { font-weight: 600; color: #1e293b; white-space: nowrap; } - /* ── 재고 ── */ + /* ?�?� ?�고 ?�?� */ .stock { font-weight: 600; white-space: nowrap; @@ -413,7 +422,7 @@ .stock.in-stock { color: #10b981; } .stock.out-stock { color: #ef4444; } - /* ── QR 버튼 ── */ + /* ?�?� QR 버튼 ?�?� */ .btn-qr { background: #8b5cf6; color: #fff; @@ -429,7 +438,7 @@ .btn-qr:hover { background: #7c3aed; } .btn-qr:active { transform: scale(0.95); } - /* ── 빈 상태 ── */ + /* ?�?� �??�태 ?�?� */ .empty-state { text-align: center; padding: 60px 20px; @@ -443,7 +452,7 @@ font-size: 15px; } - /* ── 모달 ── */ + /* ?�?� 모달 ?�?� */ .modal-overlay { display: none; position: fixed; @@ -479,7 +488,7 @@ border-radius: 8px; } - /* ── 수량 선택기 ── */ + /* ?�?� ?�량 ?�택�??�?� */ .qty-selector { display: flex; align-items: center; @@ -547,7 +556,7 @@ .modal-btn.confirm { background: #8b5cf6; color: #fff; } .modal-btn.confirm:hover { background: #7c3aed; } - /* ── 제품 이미지 ── */ + /* ?�?� ?�품 ?��?지 ?�?� */ .product-thumb { width: 40px; height: 40px; @@ -583,7 +592,7 @@ fill: #94a3b8; } - /* ── 이미지 모달 ── */ + /* ?�?� ?��?지 모달 ?�?� */ .image-modal { display: none; position: fixed; @@ -670,7 +679,7 @@ .img-modal-btn.secondary { background: #f1f5f9; color: #64748b; } .img-modal-btn.primary { background: #8b5cf6; color: #fff; } - /* ── 반응형 ── */ + /* ?�?� 반응???�?� */ @media (max-width: 768px) { .search-box { flex-direction: column; } .table-wrap { overflow-x: auto; } @@ -681,40 +690,40 @@
-

🔍 제품 검색

-

전체 제품 검색 · QR 라벨 인쇄 · 🐾 동물약 AI 상담

+

?�� ?�품 검??/h1> +

?�체 ?�품 검??· QR ?�벨 ?�쇄 · ?�� ?�물??AI ?�담

- +
- 예시 타이레놀, 벤포파워, 8806418067510, LB000001423 + ?�시 ?�?�레?�, 벤포?�워, 8806418067510, LB000001423
@@ -723,27 +732,28 @@
- - - - - - + + + + + - @@ -751,51 +761,51 @@ - +
-

🐾 동물약 AI 상담

-

심장사상충, 외부기생충, 구충제 등 무엇이든 물어보세요

+

?�� ?�물??AI ?�담

+

?�장?�상�? ?��?기생�? 구충????무엇?�든 물어보세??/p>

- - - - + + +
- 안녕하세요! 🐾 동물약 상담 AI입니다.

- 반려동물의 심장사상충 예방, 벼룩/진드기 예방, 구충제 등에 대해 무엇이든 물어보세요! + ?�녕?�세?? ?�� ?�물???�담 AI?�니??

+ 반려?�물???�장?�상�??�방, 벼룩/진드�??�방, 구충??/strong> ?�에 ?�??무엇?�든 물어보세??
- - +
- - + + - + @@ -809,7 +819,7 @@ function formatPrice(num) { if (!num) return '-'; - return new Intl.NumberFormat('ko-KR').format(num) + '원'; + return new Intl.NumberFormat('ko-KR').format(num) + '??; } function escapeHtml(str) { @@ -821,20 +831,20 @@ const search = document.getElementById('searchInput').value.trim(); const animalOnly = document.getElementById('animalOnly').checked; - // 동물약만 체크시 검색어 없어도 전체 조회 가능 + // ?�물?�만 체크??검?�어 ?�어???�체 조회 가?? if (!animalOnly) { if (!search) { - alert('검색어를 입력하세요'); + alert('검?�어�??�력?�세??); return; } if (search.length < 2) { - alert('2글자 이상 입력하세요'); + alert('2글???�상 ?�력?�세??); return; } } const tbody = document.getElementById('productsTableBody'); - tbody.innerHTML = ''; + tbody.innerHTML = ''; const inStockOnly = document.getElementById('inStockOnly').checked; let url = `/api/products?search=${encodeURIComponent(search)}`; @@ -849,11 +859,11 @@ document.getElementById('resultNum').textContent = productsData.length; renderTable(); } else { - tbody.innerHTML = ``; + tbody.innerHTML = ``; } }) .catch(err => { - tbody.innerHTML = ''; + tbody.innerHTML = ''; }); } @@ -861,18 +871,18 @@ const tbody = document.getElementById('productsTableBody'); if (productsData.length === 0) { - tbody.innerHTML = ''; + tbody.innerHTML = ''; return; } tbody.innerHTML = productsData.map((item, idx) => { - // 분류 뱃지 (동물약만) + // 분류 뱃�? (?�물?�만) const categoryBadge = item.category ? `${escapeHtml(item.category)}` : ''; - // 도매상 재고 표시 (동물약만) + // ?�매???�고 ?�시 (?�물?�만) const wsStock = (item.wholesaler_stock && item.wholesaler_stock > 0) - ? `(도매 ${item.wholesaler_stock})` + ? `(?�매 ${item.wholesaler_stock})` : ''; return ` @@ -886,7 +896,7 @@ + : `?�음`} + `}).join(''); } - // ── QR 인쇄 관련 ── + // ?�?� QR ?�쇄 관???�?� function adjustQty(delta) { printQty = Math.max(MIN_QTY, Math.min(MAX_QTY, printQty + delta)); updateQtyUI(); @@ -914,7 +925,7 @@ document.getElementById('qtyValue').textContent = printQty; document.getElementById('qtyMinus').disabled = printQty <= MIN_QTY; document.getElementById('qtyPlus').disabled = printQty >= MAX_QTY; - document.getElementById('printBtn').textContent = printQty > 1 ? `${printQty}장 인쇄` : '인쇄'; + document.getElementById('printBtn').textContent = printQty > 1 ? `${printQty}???�쇄` : '?�쇄'; } function printQR(idx) { @@ -925,12 +936,12 @@ const preview = document.getElementById('qrPreview'); const info = document.getElementById('qrInfo'); - preview.innerHTML = '

미리보기 로딩 중...

'; + preview.innerHTML = '

미리보기 로딩 �?..

'; info.innerHTML = ` ${escapeHtml(selectedItem.product_name)}
- 바코드: ${selectedItem.barcode || selectedItem.drug_code || 'N/A'}
- 가격: ${formatPrice(selectedItem.sale_price)} + 바코?? ${selectedItem.barcode || selectedItem.drug_code || 'N/A'}
+ 가�? ${formatPrice(selectedItem.sale_price)}
`; updateQtyUI(); @@ -951,11 +962,11 @@ if (data.success && data.image) { preview.innerHTML = `QR 미리보기`; } else { - preview.innerHTML = '

미리보기 실패

'; + preview.innerHTML = '

미리보기 ?�패

'; } }) .catch(() => { - preview.innerHTML = '

미리보기 오류

'; + preview.innerHTML = '

미리보기 ?�류

'; }); } @@ -976,7 +987,7 @@ let errorMsg = ''; for (let i = 0; i < totalQty; i++) { - btn.textContent = `인쇄 중... (${i + 1}/${totalQty})`; + btn.textContent = `?�쇄 �?.. (${i + 1}/${totalQty})`; try { const res = await fetch('/api/qr-print', { @@ -994,7 +1005,7 @@ if (data.success) { successCount++; } else { - errorMsg = data.error || '알 수 없는 오류'; + errorMsg = data.error || '?????�는 ?�류'; break; } @@ -1011,21 +1022,21 @@ updateQtyUI(); if (successCount === totalQty) { - alert(`✅ QR 라벨 ${totalQty}장 인쇄 완료!`); + alert(`??QR ?�벨 ${totalQty}???�쇄 ?�료!`); closeQRModal(); } else if (successCount > 0) { - alert(`⚠️ ${successCount}/${totalQty}장 인쇄 완료\n오류: ${errorMsg}`); + alert(`?�️ ${successCount}/${totalQty}???�쇄 ?�료\n?�류: ${errorMsg}`); } else { - alert(`❌ 인쇄 실패: ${errorMsg}`); + alert(`???�쇄 ?�패: ${errorMsg}`); } } - // 페이지 로드 시 검색창 포커스 + // ?�이지 로드 ??검?�창 ?�커?? document.getElementById('searchInput').focus(); - // ══════════════════════════════════════════════════════════════════ - // 동물약 챗봇 - // ══════════════════════════════════════════════════════════════════ + // ?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═ + // ?�물??챗봇 + // ?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═?�═ let chatHistory = []; let isChatLoading = false; @@ -1034,7 +1045,7 @@ const btn = document.getElementById('chatbotToggle'); const isOpen = panel.classList.toggle('open'); btn.classList.toggle('active', isOpen); - btn.innerHTML = isOpen ? '✕' : '🐾'; + btn.innerHTML = isOpen ? '?? : '?��'; if (isOpen) { document.getElementById('chatInput').focus(); } @@ -1051,14 +1062,14 @@ if (!message || isChatLoading) return; - // 사용자 메시지 표시 + // ?�용??메시지 ?�시 addChatMessage('user', message); input.value = ''; - // 히스토리에 추가 + // ?�스?�리??추�? chatHistory.push({ role: 'user', content: message }); - // 로딩 표시 + // 로딩 ?�시 isChatLoading = true; document.getElementById('chatSendBtn').disabled = true; showTypingIndicator(); @@ -1075,22 +1086,22 @@ hideTypingIndicator(); if (data.success) { - // AI 응답 표시 + // AI ?�답 ?�시 addChatMessage('assistant', data.message, data.products); - // 히스토리에 추가 + // ?�스?�리??추�? chatHistory.push({ role: 'assistant', content: data.message }); - // 히스토리 길이 제한 (최근 20개) + // ?�스?�리 길이 ?�한 (최근 20�? if (chatHistory.length > 20) { chatHistory = chatHistory.slice(-20); } } else { - addChatMessage('system', '⚠️ ' + (data.message || '오류가 발생했습니다')); + addChatMessage('system', '?�️ ' + (data.message || '?�류가 발생?�습?�다')); } } catch (error) { hideTypingIndicator(); - addChatMessage('system', '⚠️ 네트워크 오류가 발생했습니다'); + addChatMessage('system', '?�️ ?�트?�크 ?�류가 발생?�습?�다'); } isChatLoading = false; @@ -1102,19 +1113,19 @@ const msgDiv = document.createElement('div'); msgDiv.className = `chat-message ${role}`; - // 줄바꿈 처리 + // 줄바�?처리 let htmlContent = escapeHtml(content).replace(/\n/g, '
'); - // 마크다운 굵게 처리 + // 마크?�운 굵게 처리 htmlContent = htmlContent.replace(/\*\*(.+?)\*\*/g, '$1'); msgDiv.innerHTML = htmlContent; - // 언급된 제품 표시 (이미지 포함) + // ?�급???�품 ?�시 (?��?지 ?�함) if (products && products.length > 0) { const productsDiv = document.createElement('div'); productsDiv.className = 'products-mentioned'; - productsDiv.innerHTML = '📦 관련 제품:'; + productsDiv.innerHTML = '?�� 관???�품:'; const productsGrid = document.createElement('div'); productsGrid.style.cssText = 'display:flex;flex-wrap:wrap;gap:8px;margin-top:8px;'; @@ -1125,7 +1136,7 @@ card.style.cssText = 'display:flex;align-items:center;gap:8px;padding:8px;background:#f8fafc;border-radius:8px;cursor:pointer;border:1px solid #e2e8f0;'; card.onclick = () => searchProductFromChat(p.name); - // 이미지 컨테이너 + // ?��?지 컨테?�너 const imgContainer = document.createElement('div'); imgContainer.style.cssText = 'width:40px;height:40px;flex-shrink:0;'; @@ -1135,25 +1146,25 @@ img.src = p.image_url; img.alt = p.name; img.onerror = function() { - // 이미지 로드 실패 시 아이콘으로 대체 - imgContainer.innerHTML = '
💊
'; + // ?��?지 로드 ?�패 ???�이콘으�??��? + imgContainer.innerHTML = '
?��
'; }; imgContainer.appendChild(img); } else { - // 이미지 없으면 아이콘 - imgContainer.innerHTML = '
💊
'; + // ?��?지 ?�으�??�이�? + imgContainer.innerHTML = '
?��
'; } - // 텍스트 (카테고리 뱃지 + 약국/도매 재고) + // ?�스??(카테고리 뱃�? + ?�국/?�매 ?�고) const textDiv = document.createElement('div'); const pharmacyStock = p.stock || 0; const wholesalerStock = p.wholesaler_stock || 0; const stockColor = (pharmacyStock > 0) ? '#10b981' : '#ef4444'; - const pharmacyText = (pharmacyStock > 0) ? `약국 ${pharmacyStock}` : '품절'; - const wholesalerText = (wholesalerStock > 0) ? `도매 ${wholesalerStock}` : ''; + const pharmacyText = (pharmacyStock > 0) ? `?�국 ${pharmacyStock}` : '?�절'; + const wholesalerText = (wholesalerStock > 0) ? `?�매 ${wholesalerStock}` : ''; const stockDisplay = wholesalerText ? `${pharmacyText} / ${wholesalerText}` : pharmacyText; - // 카테고리 뱃지 + // 카테고리 뱃�? const categoryBadge = p.category ? `${p.category}` : ''; @@ -1189,18 +1200,18 @@ } function searchProductFromChat(productName) { - // 챗봇에서 제품 클릭 시 검색창에 입력하고 검색 + // 챗봇?�서 ?�품 ?�릭 ??검?�창???�력?�고 검?? document.getElementById('searchInput').value = productName; document.getElementById('animalOnly').checked = true; searchProducts(); - // 모바일에서 챗봇 닫기 + // 모바?�에??챗봇 ?�기 if (window.innerWidth <= 1100) { document.getElementById('chatbotPanel').classList.remove('open'); } } - // ── 이미지 등록 모달 ── + // ?�?� ?��?지 ?�록 모달 ?�?� let imgModalBarcode = null; let imgModalDrugCode = null; let imgModalName = null; @@ -1209,7 +1220,7 @@ function openImageModal(barcode, drugCode, productName) { if (!barcode && !drugCode) { - alert('제품 코드 정보가 없습니다'); + alert('?�품 코드 ?�보가 ?�습?�다'); return; } @@ -1257,7 +1268,7 @@ document.getElementById('previewBtns').style.display = 'none'; capturedImageData = null; } catch (err) { - alert('카메라에 접근할 수 없습니다'); + alert('카메?�에 ?�근?????�습?�다'); } } @@ -1298,11 +1309,11 @@ } async function submitCapturedImage() { - if (!capturedImageData) { alert('촬영된 이미지가 없습니다'); return; } + if (!capturedImageData) { alert('촬영???��?지가 ?�습?�다'); return; } const code = imgModalBarcode || imgModalDrugCode; const name = imgModalName; closeImageModal(); - showToast(`"${name}" 이미지 저장 중...`); + showToast(`"${name}" ?��?지 ?�??�?..`); try { const res = await fetch(`/api/admin/product-images/${code}/upload`, { method: 'POST', @@ -1310,19 +1321,19 @@ body: JSON.stringify({ image_data: capturedImageData, product_name: name, drug_code: imgModalDrugCode }) }); const data = await res.json(); - if (data.success) { showToast('✅ 이미지 저장 완료!', 'success'); searchProducts(); } - else showToast(data.error || '저장 실패', 'error'); - } catch (err) { showToast('오류: ' + err.message, 'error'); } + if (data.success) { showToast('???��?지 ?�???�료!', 'success'); searchProducts(); } + else showToast(data.error || '?�???�패', 'error'); + } catch (err) { showToast('?�류: ' + err.message, 'error'); } } async function submitImageUrl() { const url = document.getElementById('imgUrlInput').value.trim(); - if (!url) { alert('이미지 URL을 입력하세요'); return; } - if (!url.startsWith('http')) { alert('올바른 URL을 입력하세요'); return; } + if (!url) { alert('?��?지 URL???�력?�세??); return; } + if (!url.startsWith('http')) { alert('?�바�?URL???�력?�세??); return; } const code = imgModalBarcode || imgModalDrugCode; const name = imgModalName; closeImageModal(); - showToast(`"${name}" 이미지 다운로드 중...`); + showToast(`"${name}" ?��?지 ?�운로드 �?..`); try { const res = await fetch(`/api/admin/product-images/${code}/replace`, { method: 'POST', @@ -1330,9 +1341,9 @@ body: JSON.stringify({ image_url: url, product_name: name, drug_code: imgModalDrugCode }) }); const data = await res.json(); - if (data.success) { showToast('✅ 이미지 등록 완료!', 'success'); searchProducts(); } - else showToast(data.error || '등록 실패', 'error'); - } catch (err) { showToast('오류: ' + err.message, 'error'); } + if (data.success) { showToast('???��?지 ?�록 ?�료!', 'success'); searchProducts(); } + else showToast(data.error || '?�록 ?�패', 'error'); + } catch (err) { showToast('?�류: ' + err.message, 'error'); } } function showToast(msg, type = 'info') { @@ -1348,25 +1359,25 @@ }); - +
-

📷 제품 이미지 등록

+

?�� ?�품 ?��?지 ?�록

-
제품명
+
?�품�?/div>
코드
- - + +
- +
- +
@@ -1382,14 +1393,15 @@
- +
+
이미지상품명상품코드바코드재고판매가?��?지?�품�?/th> + ?�품코드바코??/th> + ?�치?�고?�매가 QR
-
🔍
-

상품명, 바코드, 상품코드로 검색하세요

+
+
?��
+

?�품�? 바코?? ?�품코드�?검?�하?�요

검색 중...

검??�?..

오류: ${data.error}

?�류: ${data.error}

검색 실패

검???�패

📭

검색 결과가 없습니다

?��

검??결과가 ?�습?�다

${escapeHtml(item.product_name)} - ${item.is_animal_drug ? '🐾 동물약' : ''} + ${item.is_animal_drug ? '?�� ?�물??/span>' : ''} ${categoryBadge}
${escapeHtml(item.supplier) || ''}
@@ -894,17 +904,18 @@
${item.drug_code} ${item.barcode ? `${item.barcode}` - : `없음`}${item.location ? `${escapeHtml(item.location)}` : ''} ${item.stock || 0}${wsStock} ${formatPrice(item.sale_price)} - +