Files
reseller-platform/backend/app/engines/suppliers/base.py

66 lines
2.0 KiB
Python

"""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)