shop product refactoring

This commit is contained in:
2025-10-04 21:27:48 +02:00
parent c971674ec2
commit 4d2866af5e
21 changed files with 259 additions and 220 deletions

View File

@@ -6,7 +6,8 @@ from .database.base import Base
from .database.user import User
from .database.marketplace_product import MarketplaceProduct
from .database.stock import Stock
from .database.shop import Shop, ShopProduct
from .database.shop import Shop
from .database.product import Product
from .database.marketplace_import_job import MarketplaceImportJob
# API models (Pydantic) - import the modules, not all classes
@@ -19,7 +20,7 @@ __all__ = [
"MarketplaceProduct",
"Stock",
"Shop",
"ShopProduct",
"Product",
"MarketplaceImportJob",
"api", # API models namespace
]

View File

@@ -5,7 +5,8 @@ from .base import Base
from .user import User
from .marketplace_product import MarketplaceProduct
from .stock import Stock
from .shop import Shop, ShopProduct
from .shop import Shop
from .product import Product
from .marketplace_import_job import MarketplaceImportJob
__all__ = [
@@ -14,6 +15,6 @@ __all__ = [
"MarketplaceProduct",
"Stock",
"Shop",
"ShopProduct",
"Product",
"MarketplaceImportJob",
]

View File

@@ -64,7 +64,7 @@ class MarketplaceProduct(Base, TimestampMixin):
primaryjoin="MarketplaceProduct.gtin == Stock.gtin",
viewonly=True,
)
shop_products = relationship("ShopProduct", back_populates="product")
product = relationship("Product", back_populates="marketplace_product")
# Additional indexes for marketplace queries
__table_args__ = (

View File

@@ -0,0 +1,48 @@
from datetime import datetime
from sqlalchemy import (Boolean, Column, DateTime, Float, ForeignKey, Index,
Integer, String, Text, UniqueConstraint)
from sqlalchemy.orm import relationship
# Import Base from the central database module instead of creating a new one
from app.core.database import Base
from models.database.base import TimestampMixin
class Product(Base):
__tablename__ = "products"
id = Column(Integer, primary_key=True, index=True)
shop_id = Column(Integer, ForeignKey("shops.id"), nullable=False)
marketplace_product_id = Column(Integer, ForeignKey("marketplace_products.id"), nullable=False)
# Shop-specific overrides (can override the main product data)
product_id = Column(String) # Shop's internal product ID
price = Column(Float) # Override main product price
sale_price = Column(Float)
currency = Column(String)
availability = Column(String) # Override availability
condition = Column(String)
# Shop-specific metadata
is_featured = Column(Boolean, default=False)
is_active = Column(Boolean, default=True)
display_order = Column(Integer, default=0)
# Inventory management
min_quantity = Column(Integer, default=1)
max_quantity = Column(Integer)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
shop = relationship("Shop", back_populates="product")
marketplace_product = relationship("MarketplaceProduct", back_populates="product")
# Constraints
__table_args__ = (
UniqueConstraint("shop_id", "marketplace_product_id", name="uq_product"),
Index("idx_product_active", "shop_id", "is_active"),
Index("idx_product_featured", "shop_id", "is_featured"),
)

View File

@@ -35,47 +35,7 @@ class Shop(Base, TimestampMixin):
# Relationships
owner = relationship("User", back_populates="owned_shops")
shop_products = relationship("ShopProduct", back_populates="shop")
product = relationship("Product", back_populates="shop")
marketplace_import_jobs = relationship(
"MarketplaceImportJob", back_populates="shop"
)
class ShopProduct(Base):
__tablename__ = "shop_products"
id = Column(Integer, primary_key=True, index=True)
shop_id = Column(Integer, ForeignKey("shops.id"), nullable=False)
marketplace_product_id = Column(Integer, ForeignKey("marketplace_products.id"), nullable=False)
# Shop-specific overrides (can override the main product data)
shop_product_id = Column(String) # Shop's internal product ID
shop_price = Column(Float) # Override main product price
shop_sale_price = Column(Float)
shop_currency = Column(String)
shop_availability = Column(String) # Override availability
shop_condition = Column(String)
# Shop-specific metadata
is_featured = Column(Boolean, default=False)
is_active = Column(Boolean, default=True)
display_order = Column(Integer, default=0)
# Inventory management
min_quantity = Column(Integer, default=1)
max_quantity = Column(Integer)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
shop = relationship("Shop", back_populates="shop_products")
product = relationship("MarketplaceProduct", back_populates="shop_products")
# Constraints
__table_args__ = (
UniqueConstraint("shop_id", "marketplace_product_id", name="uq_shop_product"),
Index("idx_shop_product_active", "shop_id", "is_active"),
Index("idx_shop_product_featured", "shop_id", "is_featured"),
)

37
models/schemas/product.py Normal file
View File

@@ -0,0 +1,37 @@
# product.py - Keep basic format validation, remove business logic
import re
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ConfigDict, Field, field_validator
from models.schemas.marketplace_product import MarketplaceProductResponse
class ProductCreate(BaseModel):
marketplace_product_id: str = Field(..., description="MarketplaceProduct ID to add to shop")
product_id: Optional[str] = None
price: Optional[float] = None # Removed: ge=0 constraint
sale_price: Optional[float] = None # Removed: ge=0 constraint
currency: Optional[str] = None
availability: Optional[str] = None
condition: Optional[str] = None
is_featured: bool = Field(False, description="Featured product flag")
min_quantity: int = Field(1, description="Minimum order quantity")
max_quantity: Optional[int] = None # Removed: ge=1 constraint
# Service will validate price ranges and quantity logic
class ProductResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
shop_id: int
marketplace_product: MarketplaceProductResponse
product_id: Optional[str]
price: Optional[float]
sale_price: Optional[float]
currency: Optional[str]
availability: Optional[str]
condition: Optional[str]
is_featured: bool
is_active: bool
min_quantity: int
max_quantity: Optional[int]
created_at: datetime
updated_at: datetime

View File

@@ -3,7 +3,6 @@ import re
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ConfigDict, Field, field_validator
from models.schemas.marketplace_product import MarketplaceProductResponse
class ShopCreate(BaseModel):
shop_code: str = Field(..., description="Unique shop identifier")
@@ -62,34 +61,3 @@ class ShopListResponse(BaseModel):
total: int
skip: int
limit: int
class ShopProductCreate(BaseModel):
marketplace_product_id: str = Field(..., description="MarketplaceProduct ID to add to shop")
shop_product_id: Optional[str] = None
shop_price: Optional[float] = None # Removed: ge=0 constraint
shop_sale_price: Optional[float] = None # Removed: ge=0 constraint
shop_currency: Optional[str] = None
shop_availability: Optional[str] = None
shop_condition: Optional[str] = None
is_featured: bool = Field(False, description="Featured product flag")
min_quantity: int = Field(1, description="Minimum order quantity")
max_quantity: Optional[int] = None # Removed: ge=1 constraint
# Service will validate price ranges and quantity logic
class ShopProductResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
shop_id: int
product: MarketplaceProductResponse
shop_product_id: Optional[str]
shop_price: Optional[float]
shop_sale_price: Optional[float]
shop_currency: Optional[str]
shop_availability: Optional[str]
shop_condition: Optional[str]
is_featured: bool
is_active: bool
min_quantity: int
max_quantity: Optional[int]
created_at: datetime
updated_at: datetime