"""SupplierAdapter abstract base class + registry.""" from abc import ABC, abstractmethod # Registry of adapter_type string -> adapter class. Populated by register_adapter. ADAPTERS = {} class SupplierAdapter(ABC): """Normalized interface every supplier feed must implement. Product dict shape (used by the importer): {sku, title, description, category, brand, cost, inventory, shipping_cost, shipping_time, image_url} """ adapter_type = "base" def __init__(self, supplier, data_or_url=""): self.supplier = supplier self.data_or_url = data_or_url @abstractmethod def get_products(self): """Return list of normalized product dicts.""" @abstractmethod def get_inventory(self): """Return list of {'supplier_sku': str, 'inventory': int}.""" @abstractmethod def get_price(self, sku): """Return current supplier cost for sku (number) or None.""" @abstractmethod def create_order(self, items): """Place an order at the supplier; return {'ref': str, ...}.""" @abstractmethod def get_order_status(self, ref): """Return status string for a supplier order ref.""" @abstractmethod def get_tracking(self, ref): """Return {'tracking': str, 'carrier': str} or None.""" @abstractmethod def cancel_order(self, ref): """Cancel a supplier order; return status dict.""" def register_adapter(cls): ADAPTERS[cls.adapter_type] = cls return cls def build_adapter(supplier, data_or_url=""): cls = ADAPTERS.get(supplier.adapter_type) if cls is None: # lazy-import concrete adapters so their @register_adapter side effects run from app.engines.suppliers import csv_adapter, printful, sample # noqa: F401 cls = ADAPTERS.get(supplier.adapter_type) if cls is None: raise ValueError(f"unknown adapter_type '{supplier.adapter_type}' for supplier {supplier.name}") return cls(supplier, data_or_url=data_or_url)