File size: 3,887 Bytes
0a8c81b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | import logging
logger = logging.getLogger(__name__)
from src.adapters.alibaba_adapter import Alibaba1688Adapter
from src.adapters.source2_adapter import Source2Adapter
from src.adapters.source3_adapter import Source3Adapter
import random
class MultiSourceAdapter:
"""
Multi-Source Procurement Engine.
Executes a waterfall fallback strategy: P1 (1688) -> P2 (Source 2) -> P3 (Source 3).
Includes hard Loss Prevention (Margin Protection) logic.
"""
def __init__(self):
self.adapters = {
"1688": Alibaba1688Adapter(),
"source2": Source2Adapter(),
"source3": Source3Adapter()
}
def find_and_draft_order(self, shopify_sku: str, shopify_title: str, quantity: int, address: dict, max_allowed_cost_cny: float = 0.0) -> dict:
"""
Attempts to map and draft an order by cascading through sources.
"""
platforms = ["1688", "source2", "source3"]
logger.info(f"🌊 [MULTI-SOURCE ENGINE] Starting procurement cascade for '{shopify_sku}' (Max Allowed Cost: ¥{max_allowed_cost_cny})")
for platform in platforms:
adapter = self.adapters[platform]
logger.info(f"🔍 [MULTI-SOURCE ENGINE] Probing P{platforms.index(platform)+1} Platform: {platform.upper()}")
# Simulate a 50% chance of stockout on 1688 to test fallback
if platform == "1688" and random.random() < 0.5:
logger.warning(f"⚠️ [MULTI-SOURCE ENGINE] {platform.upper()} reported STOCKOUT. Cascading to next platform...")
continue
try:
# 1. Map SKU
if platform == "1688":
mapping = adapter.map_shopify_sku_to_1688(shopify_sku, shopify_title)
else:
mapping = adapter.map_shopify_sku(shopify_sku, shopify_title)
# 2. Margin Protection Check (Mocking the price check)
# In real life, mapping result would return current price. We mock a random cost.
simulated_cost = random.uniform(20.0, 150.0)
logger.info(f"💰 [MULTI-SOURCE ENGINE] Platform {platform.upper()} returned cost: ¥{simulated_cost:.2f}")
if max_allowed_cost_cny > 0 and simulated_cost > max_allowed_cost_cny:
logger.error(f"🚨 [MARGIN PROTECTION] Cost ¥{simulated_cost:.2f} exceeds max allowed ¥{max_allowed_cost_cny:.2f}. Bypassing {platform.upper()}...")
continue
# 3. Draft Order
draft = adapter.create_draft_order(mapping["variant_id"], quantity, address)
logger.info(f"✅ [MULTI-SOURCE ENGINE] Successfully procured via {platform.upper()}!")
return {
"status": "SUCCESS",
"sourcing_platform": platform.upper(),
"supplier_id": mapping["supplier_id"],
"variant_id": mapping["variant_id"],
"draft_order_id": draft["order_id"],
"estimated_shipping_cny": draft["estimated_shipping_cny"],
"actual_cost_cny": simulated_cost
}
except Exception as e:
logger.error(f"❌ [MULTI-SOURCE ENGINE] {platform.upper()} encountered an error: {str(e)}. Cascading...")
continue
# If we exhausted all platforms
logger.error(f"🚨 [MULTI-SOURCE ENGINE] ALL PLATFORMS EXHAUSTED OR COST TOO HIGH. Sourcing Failed for '{shopify_sku}'")
return {
"status": "SOURCING_FAILED",
"reason": "Out of stock on all platforms or all options lead to a net loss."
}
|