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