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." }