diff --git a/backend/order_api.py b/backend/order_api.py index a8ad0af..58fef7e 100644 --- a/backend/order_api.py +++ b/backend/order_api.py @@ -152,14 +152,21 @@ def api_submit_order(): return jsonify(result) -def submit_geoyoung_order(order: dict, dry_run: bool) -> dict: - """지오영 주문 제출""" +def submit_geoyoung_order(order: dict, dry_run: bool, cart_only: bool = True) -> dict: + """ + 지오영 주문 제출 + + Args: + order: 주문 정보 + dry_run: True=시뮬레이션만, False=실제 주문 + cart_only: True=장바구니만, False=주문 확정까지 + """ order_id = order['id'] items = order['items'] # 상태 업데이트 update_order_status(order_id, 'pending', - f'주문 제출 시작 (dry_run={dry_run})') + f'주문 제출 시작 (dry_run={dry_run}, cart_only={cart_only})') results = [] success_count = 0 @@ -286,20 +293,22 @@ def submit_geoyoung_order(order: dict, dry_run: bool) -> dict: spec = item.get('specification', '') try: - # 지오영 주문 실행 (빠른 API - 장바구니+확정) + # 지오영 주문 실행 + # cart_only=True: 장바구니만 (auto_confirm=False) + # cart_only=False: 주문 확정까지 (auto_confirm=True) result = geo_session.full_order( kd_code=kd_code, quantity=order_qty, specification=spec if spec else None, check_stock=True, - auto_confirm=True, + auto_confirm=not cart_only, # cart_only면 확정 안함 memo=f"자동주문 - {item.get('product_name', '')}" ) if result.get('success'): status = 'success' - result_code = 'OK' - result_message = result.get('message', '주문 완료') + result_code = 'CART_ADDED' if cart_only else 'OK' + result_message = '장바구니 추가 완료' if cart_only else result.get('message', '주문 완료') success_count += 1 else: status = 'failed' @@ -338,25 +347,41 @@ def submit_geoyoung_order(order: dict, dry_run: bool) -> dict: }) # 주문 상태 업데이트 - if failed_count == 0: - update_order_status(order_id, 'submitted', - f'주문 제출 완료: {success_count}개 품목') - elif success_count == 0: - update_order_status(order_id, 'failed', - f'주문 실패: {failed_count}개 품목') + if cart_only: + # 장바구니만 담은 경우 + if failed_count == 0: + update_order_status(order_id, 'pending', + f'지오영 장바구니 추가 완료: {success_count}개 (사이트에서 확정 필요)') + elif success_count == 0: + update_order_status(order_id, 'failed', + f'장바구니 추가 실패: {failed_count}개 품목') + else: + update_order_status(order_id, 'partial', + f'부분 성공: {success_count}개 장바구니, {failed_count}개 실패') else: - update_order_status(order_id, 'partial', - f'부분 주문: {success_count}개 성공, {failed_count}개 실패') + # 실제 주문까지 한 경우 + if failed_count == 0: + update_order_status(order_id, 'submitted', + f'주문 제출 완료: {success_count}개 품목') + elif success_count == 0: + update_order_status(order_id, 'failed', + f'주문 실패: {failed_count}개 품목') + else: + update_order_status(order_id, 'partial', + f'부분 주문: {success_count}개 성공, {failed_count}개 실패') return { 'success': True, 'dry_run': dry_run, + 'cart_only': cart_only, 'order_id': order_id, 'order_no': order['order_no'], + 'wholesaler': 'geoyoung', 'total_items': len(items), 'success_count': success_count, 'failed_count': failed_count, - 'results': results + 'results': results, + 'note': '지오영 장바구니에 담김. 지오영 사이트에서 최종 확정 필요.' if cart_only else None } except Exception as e: @@ -409,9 +434,10 @@ def api_quick_submit(): POST /api/order/quick-submit { - "wholesaler_id": "geoyoung" | "sooin", + "wholesaler_id": "geoyoung" | "sooin" | "baekje", "items": [...], - "dry_run": true + "dry_run": true, + "cart_only": true // true=장바구니만, false=실제 주문까지 } """ data = request.get_json() @@ -438,13 +464,14 @@ def api_quick_submit(): # 3. 주문 제출 dry_run = data.get('dry_run', True) + cart_only = data.get('cart_only', True) # 기본값: 장바구니만 if order['wholesaler_id'] == 'geoyoung': - submit_result = submit_geoyoung_order(order, dry_run) + submit_result = submit_geoyoung_order(order, dry_run, cart_only=cart_only) elif order['wholesaler_id'] == 'sooin': - submit_result = submit_sooin_order(order, dry_run) + submit_result = submit_sooin_order(order, dry_run, cart_only=cart_only) elif order['wholesaler_id'] == 'baekje': - submit_result = submit_baekje_order(order, dry_run) + submit_result = submit_baekje_order(order, dry_run, cart_only=cart_only) else: submit_result = {'success': False, 'error': f"Wholesaler {order['wholesaler_id']} not supported"} @@ -453,14 +480,21 @@ def api_quick_submit(): return jsonify(submit_result) -def submit_sooin_order(order: dict, dry_run: bool) -> dict: - """수인약품 주문 제출""" +def submit_sooin_order(order: dict, dry_run: bool, cart_only: bool = True) -> dict: + """ + 수인약품 주문 제출 + + Args: + order: 주문 정보 + dry_run: True=시뮬레이션만, False=실제 주문 + cart_only: True=장바구니만, False=주문 확정까지 + """ order_id = order['id'] items = order['items'] # 상태 업데이트 update_order_status(order_id, 'pending', - f'수인 주문 시작 (dry_run={dry_run})') + f'수인 주문 시작 (dry_run={dry_run}, cart_only={cart_only})') results = [] success_count = 0 @@ -572,23 +606,15 @@ def submit_sooin_order(order: dict, dry_run: bool) -> dict: kd_code = item.get('kd_code') or item.get('drug_code') order_qty = item['order_qty'] spec = item.get('specification', '') - internal_code = item.get('internal_code') try: - # internal_code가 없으면 검색해서 찾기 - if not internal_code: - search_result = sooin_session.search_products(kd_code) - if search_result.get('success'): - for sooin_item in search_result.get('items', []): - if spec in sooin_item.get('spec', '') or sooin_item.get('spec', '') in spec: - internal_code = sooin_item.get('internal_code') - break - - if not internal_code: - raise ValueError(f"내부 코드를 찾을 수 없음: {kd_code} {spec}") - - # 장바구니 추가 - cart_result = sooin_session.add_to_cart(internal_code, order_qty) + # quick_order 사용 (검색 → 가격/재고 정보 포함하여 장바구니 추가) + cart_result = sooin_session.quick_order( + kd_code=kd_code, + quantity=order_qty, + spec=spec if spec else None, + check_stock=False # 재고 체크는 이미 테스트에서 했으므로 스킵 + ) if cart_result.get('success'): status = 'success' @@ -609,6 +635,9 @@ def submit_sooin_order(order: dict, dry_run: bool) -> dict: update_item_result(item['id'], status, result_code, result_message) + # quick_order 결과에서 internal_code 가져오기 + internal_code = cart_result.get('product', {}).get('internal_code') if cart_result.get('success') else None + save_order_context(item['id'], { 'drug_code': item['drug_code'], 'product_name': item['product_name'], @@ -629,20 +658,57 @@ def submit_sooin_order(order: dict, dry_run: bool) -> dict: 'order_qty': order_qty, 'status': status, 'result_code': result_code, - 'result_message': result_message + 'result_message': result_message, + 'internal_code': internal_code # 선별 주문용 }) - # 주문 확정은 별도로 (장바구니에 담기만 한 상태) - if success_count > 0: + # cart_only=False면 주문 확정까지 진행 (선별 주문!) + if not cart_only and success_count > 0: + try: + # 이번에 담은 품목의 internal_code만 수집 + ordered_codes = [r['internal_code'] for r in results + if r['status'] == 'success' and r.get('internal_code')] + + if ordered_codes: + # 선별 주문: 기존 품목은 건드리지 않고, 이번에 담은 것만 주문 + confirm_result = sooin_session.submit_order_selective(ordered_codes) + + if confirm_result.get('success'): + restored_info = f", 기존 {confirm_result.get('restored_count', 0)}개 복원" if confirm_result.get('restored_count', 0) > 0 else "" + update_order_status(order_id, 'submitted', + f'수인 주문 확정 완료: {success_count}개{restored_info}') + # 결과 메시지 업데이트 + for r in results: + if r['status'] == 'success': + r['result_code'] = 'OK' + r['result_message'] = '주문 확정 완료' + else: + update_order_status(order_id, 'partial', + f'수인 장바구니 담김, 확정 실패: {confirm_result.get("error", "알 수 없는 오류")}') + else: + update_order_status(order_id, 'partial', + f'수인 장바구니 담김, internal_code 없음') + except Exception as e: + logger.error(f"수인 주문 확정 오류: {e}") + update_order_status(order_id, 'partial', + f'수인 장바구니 담김, 확정 중 오류: {str(e)}') + elif success_count > 0: update_order_status(order_id, 'pending', f'수인 장바구니 추가 완료: {success_count}개 (확정 필요)') else: update_order_status(order_id, 'failed', f'수인 주문 실패: {failed_count}개') + # 응답 생성 + if cart_only: + note = '수인약품 장바구니에 담김. 사이트에서 최종 확정 필요.' + else: + note = None + return { 'success': True, 'dry_run': dry_run, + 'cart_only': cart_only, 'order_id': order_id, 'order_no': order['order_no'], 'wholesaler': 'sooin', @@ -650,7 +716,7 @@ def submit_sooin_order(order: dict, dry_run: bool) -> dict: 'success_count': success_count, 'failed_count': failed_count, 'results': results, - 'note': '실제 주문 시 장바구니에 담김. 수인약품 사이트에서 최종 확정 필요.' if not dry_run else None + 'note': note if not dry_run else None } except Exception as e: @@ -663,14 +729,21 @@ def submit_sooin_order(order: dict, dry_run: bool) -> dict: } -def submit_baekje_order(order: dict, dry_run: bool) -> dict: - """백제약품 주문 제출""" +def submit_baekje_order(order: dict, dry_run: bool, cart_only: bool = True) -> dict: + """ + 백제약품 주문 제출 + + Args: + order: 주문 정보 + dry_run: True=시뮬레이션만, False=실제 주문 + cart_only: True=장바구니만, False=주문 확정까지 + """ order_id = order['id'] items = order['items'] # 상태 업데이트 update_order_status(order_id, 'pending', - f'백제약품 주문 시작 (dry_run={dry_run})') + f'백제약품 주문 시작 (dry_run={dry_run}, cart_only={cart_only})') results = [] success_count = 0 @@ -828,17 +901,42 @@ def submit_baekje_order(order: dict, dry_run: bool) -> dict: 'result_message': result_message }) - # 상태 업데이트 - if success_count > 0: + # cart_only=False면 주문 확정까지 진행 + if not cart_only and success_count > 0: + try: + confirm_result = baekje_session.submit_order() + if confirm_result.get('success'): + update_order_status(order_id, 'submitted', + f'백제 주문 확정 완료: {success_count}개') + # 결과 메시지 업데이트 + for r in results: + if r['status'] == 'success': + r['result_code'] = 'OK' + r['result_message'] = '주문 확정 완료' + else: + update_order_status(order_id, 'partial', + f'백제 장바구니 담김, 확정 실패: {confirm_result.get("error", "알 수 없는 오류")}') + except Exception as e: + logger.error(f"백제 주문 확정 오류: {e}") + update_order_status(order_id, 'partial', + f'백제 장바구니 담김, 확정 중 오류: {str(e)}') + elif success_count > 0: update_order_status(order_id, 'pending', f'백제 장바구니 추가 완료: {success_count}개 (확정 필요)') else: update_order_status(order_id, 'failed', f'백제 주문 실패: {failed_count}개') + # 응답 생성 + if cart_only: + note = '백제약품 장바구니에 담김. 백제몰(ibjp.co.kr)에서 최종 확정 필요.' + else: + note = None + return { 'success': True, 'dry_run': dry_run, + 'cart_only': cart_only, 'order_id': order_id, 'order_no': order['order_no'], 'wholesaler': 'baekje', @@ -846,7 +944,7 @@ def submit_baekje_order(order: dict, dry_run: bool) -> dict: 'success_count': success_count, 'failed_count': failed_count, 'results': results, - 'note': '실제 주문 시 장바구니에 담김. 백제몰(ibjp.co.kr)에서 최종 확정 필요.' if not dry_run else None + 'note': note if not dry_run else None } except Exception as e: diff --git a/backend/templates/admin_rx_usage.html b/backend/templates/admin_rx_usage.html index d8a58dd..6ef66df 100644 --- a/backend/templates/admin_rx_usage.html +++ b/backend/templates/admin_rx_usage.html @@ -528,6 +528,25 @@ color: var(--text-muted); margin-top: 4px; } + .cart-item-actions { + display: flex; + gap: 6px; + align-items: center; + } + .cart-item-order { + background: rgba(16, 185, 129, 0.2); + border: none; + color: var(--accent-emerald); + padding: 4px 8px; + border-radius: 6px; + cursor: pointer; + font-size: 11px; + font-weight: 600; + white-space: nowrap; + } + .cart-item-order:hover { + background: rgba(16, 185, 129, 0.4); + } .cart-item-remove { background: rgba(244, 63, 94, 0.2); border: none; @@ -1049,7 +1068,10 @@
${escapeHtml(item.product_name)}
${item.supplier || '-'} · ${item.qty}개
- +
+ + +
`).join(''); } @@ -1131,28 +1153,256 @@ } } + // 다중 도매상 선택을 위한 전역 변수 + let pendingWholesalerItems = {}; + let pendingOtherItems = []; + function openWholesalerSelectModal(itemsByWholesaler, otherItems) { - let msg = `장바구니에 여러 도매상 품목이 있습니다.\n\n`; + pendingWholesalerItems = itemsByWholesaler; + pendingOtherItems = otherItems; + + const modal = document.getElementById('multiWholesalerModal'); + const body = document.getElementById('multiWholesalerBody'); const wsIds = Object.keys(itemsByWholesaler); + + let html = ` +
+

+ 장바구니에 ${wsIds.length}개 도매상의 품목이 있습니다. +

+ `; + + // 각 도매상별 품목 표시 wsIds.forEach(wsId => { const ws = WHOLESALERS[wsId]; - msg += `${ws.icon} ${ws.name}: ${itemsByWholesaler[wsId].length}개\n`; + const items = itemsByWholesaler[wsId]; + html += ` +
+
+ ${ws.icon} + ${ws.name} + ${items.length}개 품목 + +
+
+ ${items.slice(0, 3).map(item => ` +
· ${item.product_name} (${item.qty}개)
+ `).join('')} + ${items.length > 3 ? `
... 외 ${items.length - 3}개
` : ''} +
+
+ `; }); if (otherItems.length > 0) { - msg += `📋 기타: ${otherItems.length}개\n`; + html += ` +
+
+ 📋 + 기타 (API 미지원) + ${otherItems.length}개 품목 +
+
+
클립보드 복사로 처리됩니다
+
+
+ `; } - msg += `\n어느 도매상부터 주문하시겠습니까?`; - msg += `\n\n[확인] = ${WHOLESALERS[wsIds[0]].name} 먼저`; - msg += `\n[취소] = ${WHOLESALERS[wsIds[1]].name} 먼저`; + html += `
`; - if (confirm(msg)) { - openOrderConfirmModal(wsIds[0], itemsByWholesaler[wsIds[0]]); - } else { - openOrderConfirmModal(wsIds[1], itemsByWholesaler[wsIds[1]]); + body.innerHTML = html; + modal.classList.add('show'); + } + + function closeMultiWholesalerModal() { + document.getElementById('multiWholesalerModal').classList.remove('show'); + } + + // 선택된 도매상 전체 일괄 처리 + async function executeAllWholesalers(dryRun = false) { + const wsIds = Object.keys(pendingWholesalerItems); + + // 체크된 도매상만 필터 + const selectedWsIds = wsIds.filter(wsId => { + const checkbox = document.getElementById(`ws_check_${wsId}`); + return checkbox && checkbox.checked; + }); + + if (selectedWsIds.length === 0) { + showToast('선택된 도매상이 없습니다', 'error'); + return; } + + // 버튼 비활성화 + const btnTest = document.getElementById('btnMultiTest'); + const btnReal = document.getElementById('btnMultiReal'); + btnTest.disabled = true; + btnReal.disabled = true; + btnReal.textContent = '처리 중...'; + + const allResults = []; + let totalSuccess = 0; + let totalFailed = 0; + + // 각 도매상 순차 처리 + for (const wsId of selectedWsIds) { + const ws = WHOLESALERS[wsId]; + const items = pendingWholesalerItems[wsId]; + + showToast(`${ws.icon} ${ws.name} 처리 중... (${selectedWsIds.indexOf(wsId) + 1}/${selectedWsIds.length})`, 'info'); + + try { + const payload = { + wholesaler_id: wsId, + items: items.map(item => ({ + drug_code: item.drug_code, + kd_code: item.geoyoung_code || item.sooin_code || item.drug_code, + internal_code: item.internal_code, + product_name: item.product_name, + manufacturer: item.supplier, + specification: item.specification || '', + order_qty: item.qty, + usage_qty: item.usage_qty || 0, + current_stock: item.current_stock || 0 + })), + reference_period: `${document.getElementById('startDate').value}~${document.getElementById('endDate').value}`, + dry_run: dryRun, + cart_only: false // 장바구니 + 주문 확정까지 + }; + + const response = await fetch('/api/order/quick-submit', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(payload), + signal: AbortSignal.timeout(180000) // 주문 확정까지 3분 + }); + + const result = await response.json(); + result.wholesaler_id = wsId; + allResults.push(result); + + if (result.success) { + totalSuccess += result.success_count || 0; + totalFailed += result.failed_count || 0; + } else { + totalFailed += items.length; + } + + } catch (err) { + allResults.push({ + wholesaler_id: wsId, + success: false, + error: err.message, + success_count: 0, + failed_count: items.length + }); + totalFailed += items.length; + } + } + + // 기타 품목 클립보드 처리 + if (pendingOtherItems.length > 0) { + submitOrderClipboardItems(pendingOtherItems); + } + + closeMultiWholesalerModal(); + showMultiOrderResultModal(allResults, totalSuccess, totalFailed, dryRun); + + // 버튼 복원 + btnTest.disabled = false; + btnReal.disabled = false; + btnReal.textContent = '📤 전체 주문 전송'; + } + + // 특정 품목만 클립보드 복사 + function submitOrderClipboardItems(items) { + let orderText = `📋 기타 품목 (API 미지원)\n━━━━━━━━━━━━━━━━━━\n`; + items.forEach((item, i) => { + orderText += `${i+1}. ${item.product_name} - ${item.qty}개\n`; + }); + navigator.clipboard.writeText(orderText); + } + + // 다중 도매상 결과 모달 + function showMultiOrderResultModal(results, totalSuccess, totalFailed, isDryRun) { + const modal = document.getElementById('orderResultModal'); + const content = document.getElementById('orderResultContent'); + const header = modal.querySelector('.order-modal-header h3'); + const headerDiv = modal.querySelector('.order-modal-header'); + + headerDiv.style.background = 'linear-gradient(135deg, #059669, #10b981)'; + header.innerHTML = '📋 전체 주문 결과'; + + const statusEmoji = totalFailed === 0 ? '✅' : totalSuccess === 0 ? '❌' : '⚠️'; + const modeText = isDryRun ? '[테스트]' : ''; + + let html = ` +
+ ${statusEmoji} + ${modeText} ${results.length}개 도매상 처리 완료 +
+
+
+ 도매상 + ${results.length}개 +
+
+ 총 성공 + ${totalSuccess}개 +
+
+ 총 실패 + ${totalFailed}개 +
+
+ `; + + // 각 도매상별 결과 + results.forEach(result => { + const wsId = result.wholesaler_id || result.wholesaler; + const ws = WHOLESALERS[wsId] || {icon: '📦', name: wsId, gradient: 'var(--bg-card)'}; + const isSuccess = result.success && result.failed_count === 0; + + html += ` +
+
+ ${ws.icon} + ${ws.name} + + ${isSuccess ? '✓ 완료' : '⚠ 일부실패'} + +
+
+ ${result.success ? ` +
+ 성공: ${result.success_count}개 / 실패: ${result.failed_count}개 + ${result.order_no ? ` · 주문번호: ${result.order_no}` : ''} +
+ ` : ` +
+ 오류: ${result.error || '처리 실패'} +
+ `} +
+
+ `; + }); + + if (isDryRun) { + html += `
💡 테스트 모드입니다. "전체 장바구니 담기" 버튼으로 실제 진행하세요.
`; + } else if (totalSuccess > 0) { + html += `
+ 🛒 각 도매상 장바구니에 담겼습니다. 각 사이트에서 최종 확정이 필요합니다. +
`; + } + + content.innerHTML = html; + modal.classList.add('show'); } function openOrderConfirmModal(wholesalerId, items) { @@ -1188,6 +1438,72 @@ currentOrderWholesaler = null; } + // ──────────────── 개별 품목 실제 주문 ──────────────── + async function orderSingleItem(drugCode) { + const item = cart.find(i => i.drug_code === drugCode); + if (!item) { + showToast('품목을 찾을 수 없습니다', 'error'); + return; + } + + // 도매상 결정 + let wholesaler = 'geoyoung'; + for (const [wsId, ws] of Object.entries(WHOLESALERS)) { + if (ws.filterFn(item)) { + wholesaler = wsId; + break; + } + } + + const ws = WHOLESALERS[wholesaler]; + + // 확인 다이얼로그 + if (!confirm(`${ws.icon} ${ws.name}에 실제 주문하시겠습니까?\n\n품목: ${item.product_name}\n수량: ${item.qty}개\n\n⚠️ 이 작업은 실제 주문을 진행합니다!`)) { + return; + } + + showToast(`📤 ${item.product_name} 주문 중...`, 'info'); + + try { + const payload = { + wholesaler_id: wholesaler, + items: [{ + drug_code: item.drug_code, + kd_code: item.geoyoung_code || item.sooin_code || item.drug_code, + internal_code: item.internal_code, + product_name: item.product_name, + manufacturer: item.supplier, + specification: item.specification || '', + order_qty: item.qty, + usage_qty: item.usage_qty || 0, + current_stock: item.current_stock || 0 + }], + reference_period: `${document.getElementById('startDate').value}~${document.getElementById('endDate').value}`, + dry_run: false, + cart_only: false // 실제 주문까지! + }; + + const response = await fetch('/api/order/quick-submit', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(payload) + }); + + const result = await response.json(); + + if (result.success && result.success_count > 0) { + showToast(`✅ ${item.product_name} 주문 완료!`, 'success'); + // 장바구니에서 제거 + removeFromCart(drugCode); + } else { + const errorMsg = result.results?.[0]?.result_message || result.error || '주문 실패'; + showToast(`❌ ${errorMsg}`, 'error'); + } + } catch (err) { + showToast(`❌ 오류: ${err.message}`, 'error'); + } + } + async function executeOrder(dryRun = true) { const wholesaler = currentOrderWholesaler || 'geoyoung'; @@ -1206,7 +1522,7 @@ btnTest.disabled = true; btnReal.disabled = true; btnTest.textContent = dryRun ? '처리 중...' : '🧪 테스트'; - btnReal.textContent = !dryRun ? '처리 중...' : '🚀 실제 주문'; + btnReal.textContent = !dryRun ? '처리 중...' : '🛒 장바구니 담기'; try { const payload = { @@ -1223,7 +1539,8 @@ current_stock: item.current_stock || 0 })), reference_period: `${document.getElementById('startDate').value}~${document.getElementById('endDate').value}`, - dry_run: dryRun + dry_run: dryRun, + cart_only: false // 장바구니 + 주문 확정까지 }; // 타임아웃 설정 @@ -1257,7 +1574,7 @@ btnTest.disabled = false; btnReal.disabled = false; btnTest.textContent = '🧪 테스트'; - btnReal.textContent = '🚀 실제 주문'; + btnReal.textContent = '📤 주문 전송'; } } @@ -1276,12 +1593,23 @@ headerDiv.style.background = ws.gradient; const isDryRun = result.dry_run; + const isCartOnly = result.cart_only; const statusEmoji = result.failed_count === 0 ? '✅' : result.success_count === 0 ? '❌' : '⚠️'; + // 결과 타이틀 + let resultTitle = ''; + if (isDryRun) { + resultTitle = `[테스트] ${ws.name} 시뮬레이션 완료`; + } else if (isCartOnly) { + resultTitle = `${ws.name} 장바구니 담기 ${result.failed_count === 0 ? '완료' : '처리됨'}`; + } else { + resultTitle = `${ws.name} 주문 ${result.failed_count === 0 ? '완료' : '처리됨'}`; + } + let html = `
${statusEmoji} - ${isDryRun ? '[테스트]' : ''} ${ws.name} 주문 ${result.failed_count === 0 ? '완료' : '처리됨'} + ${resultTitle}
@@ -1322,12 +1650,21 @@ html += ''; if (isDryRun && result.success_count > 0) { - html += `
💡 테스트 모드입니다. 실제 주문은 "실제 주문" 버튼을 누르세요.
`; + html += `
💡 테스트 모드입니다. "장바구니 담기" 버튼으로 실제 진행하세요.
`; } - // 수인/백제 실제 주문 시 안내 + // 장바구니만 담은 경우 안내 + if (!isDryRun && isCartOnly && result.success_count > 0) { + html += `
+ 🛒 ${ws.name} 장바구니에 담겼습니다. 사이트에서 최종 확정이 필요합니다. + ${wholesalerId === 'geoyoung' ? '
개별 품목은 📤주문 버튼으로 바로 주문할 수 있습니다.' : ''} +
`; + } + + // 도매상별 안내 if (!isDryRun && result.note) { const noteColors = { + geoyoung: 'rgba(6,182,212,0.1);color:#06b6d4', sooin: 'rgba(168,85,247,0.1);color:#a855f7', baekje: 'rgba(245,158,11,0.1);color:#f59e0b' }; @@ -2113,6 +2450,79 @@ font-size: 12px; color: var(--accent-cyan); } + + /* 다중 도매상 선택 모달 스타일 */ + .multi-ws-summary { + padding: 0 4px; + } + .multi-ws-card { + background: var(--bg-card); + border-radius: 12px; + margin-bottom: 12px; + overflow: hidden; + border: 1px solid var(--border); + } + .multi-ws-card.geoyoung { + border-left: 3px solid var(--accent-cyan); + } + .multi-ws-card.sooin { + border-left: 3px solid var(--accent-purple); + } + .multi-ws-card.baekje { + border-left: 3px solid var(--accent-amber); + } + .multi-ws-card.other { + border-left: 3px solid var(--text-muted); + opacity: 0.7; + } + .multi-ws-header { + display: flex; + align-items: center; + gap: 10px; + padding: 14px 16px; + background: rgba(255,255,255,0.02); + } + .multi-ws-icon { + font-size: 20px; + } + .multi-ws-name { + font-weight: 600; + font-size: 14px; + flex: 1; + } + .multi-ws-count { + font-size: 12px; + color: var(--text-muted); + background: rgba(255,255,255,0.1); + padding: 3px 10px; + border-radius: 12px; + } + .multi-ws-checkbox { + display: flex; + align-items: center; + gap: 6px; + font-size: 12px; + color: var(--accent-emerald); + cursor: pointer; + } + .multi-ws-checkbox input { + width: 18px; + height: 18px; + accent-color: var(--accent-emerald); + } + .multi-ws-items { + padding: 10px 16px 14px; + border-top: 1px solid rgba(255,255,255,0.05); + } + .multi-ws-item { + font-size: 12px; + color: var(--text-secondary); + padding: 3px 0; + } + .multi-ws-item.more { + color: var(--text-muted); + font-style: italic; + } @@ -2124,7 +2534,10 @@

- 0개 품목을 지오영에 주문합니다. + 0개 품목을 지오영 장바구니에 담습니다. +

+

+ ⚠️ 장바구니 담기만 진행됩니다. 도매상 사이트에서 최종 확정이 필요합니다.

@@ -2133,7 +2546,7 @@ @@ -2153,6 +2566,22 @@ + +
+
+
+

🛒 전체 도매상 주문

+ +
+
+
+ +
+
+
품목명규격수량