✅ New functionality added (soft delete system) ✅ Backward compatible (existing features unchanged) ✅ Significant enhancement (complete temporal tracking system) ✅ API additions (new endpoints, parameters) ✅ UI enhancements (new components, visual indicators)
		
			
				
	
	
		
			255 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			255 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from pydantic import BaseModel, Field
 | |
| from typing import Optional, List
 | |
| from datetime import datetime, date
 | |
| 
 | |
| # Brand schemas
 | |
| class BrandBase(BaseModel):
 | |
|     name: str
 | |
| 
 | |
| class BrandCreate(BrandBase):
 | |
|     pass
 | |
| 
 | |
| class BrandUpdate(BaseModel):
 | |
|     name: Optional[str] = None
 | |
| 
 | |
| class Brand(BrandBase):
 | |
|     id: int
 | |
|     created_at: datetime
 | |
|     updated_at: Optional[datetime] = None
 | |
|     
 | |
|     class Config:
 | |
|         from_attributes = True
 | |
| 
 | |
| # BrandInShop schemas
 | |
| class BrandInShopBase(BaseModel):
 | |
|     shop_id: int
 | |
|     brand_id: int
 | |
| 
 | |
| class BrandInShopCreate(BrandInShopBase):
 | |
|     pass
 | |
| 
 | |
| class BrandInShopUpdate(BaseModel):
 | |
|     shop_id: Optional[int] = None
 | |
|     brand_id: Optional[int] = None
 | |
| 
 | |
| class BrandInShop(BrandInShopBase):
 | |
|     id: int
 | |
|     created_at: datetime
 | |
|     updated_at: Optional[datetime] = None
 | |
|     shop: "Shop"
 | |
|     brand: "Brand"
 | |
|     
 | |
|     class Config:
 | |
|         from_attributes = True
 | |
| 
 | |
| # Grocery Category schemas
 | |
| class GroceryCategoryBase(BaseModel):
 | |
|     name: str
 | |
| 
 | |
| class GroceryCategoryCreate(GroceryCategoryBase):
 | |
|     pass
 | |
| 
 | |
| class GroceryCategoryUpdate(BaseModel):
 | |
|     name: Optional[str] = None
 | |
| 
 | |
| class GroceryCategory(GroceryCategoryBase):
 | |
|     id: int
 | |
|     created_at: datetime
 | |
|     updated_at: Optional[datetime] = None
 | |
|     
 | |
|     class Config:
 | |
|         from_attributes = True
 | |
| 
 | |
| # Product schemas
 | |
| class ProductBase(BaseModel):
 | |
|     name: str
 | |
|     category_id: int
 | |
|     brand_id: Optional[int] = None
 | |
|     organic: bool = False
 | |
|     weight: Optional[float] = None
 | |
|     weight_unit: str = "g"
 | |
| 
 | |
| class ProductCreate(ProductBase):
 | |
|     valid_from: Optional[date] = None  # If not provided, will use current date
 | |
| 
 | |
| class ProductUpdate(BaseModel):
 | |
|     name: Optional[str] = None
 | |
|     category_id: Optional[int] = None
 | |
|     brand_id: Optional[int] = None
 | |
|     organic: Optional[bool] = None
 | |
|     weight: Optional[float] = None
 | |
|     weight_unit: Optional[str] = None
 | |
|     valid_from: Optional[date] = None  # If not provided, will use current date
 | |
| 
 | |
| class Product(ProductBase):
 | |
|     id: int
 | |
|     created_at: datetime
 | |
|     updated_at: Optional[datetime] = None
 | |
|     category: GroceryCategory
 | |
|     brand: Optional[Brand] = None
 | |
|     
 | |
|     class Config:
 | |
|         from_attributes = True
 | |
| 
 | |
| # Historical Product schemas
 | |
| class ProductHistory(BaseModel):
 | |
|     history_id: int
 | |
|     id: int  # Original product ID
 | |
|     name: str
 | |
|     category_id: int
 | |
|     brand_id: Optional[int] = None
 | |
|     organic: bool = False
 | |
|     weight: Optional[float] = None
 | |
|     weight_unit: str = "g"
 | |
|     created_at: Optional[datetime] = None
 | |
|     updated_at: Optional[datetime] = None
 | |
|     valid_from: date
 | |
|     valid_to: date
 | |
|     deleted: bool
 | |
|     operation: str  # 'U' for Update, 'D' for Delete
 | |
|     archived_at: datetime
 | |
|     
 | |
|     class Config:
 | |
|         from_attributes = True
 | |
| 
 | |
| class ProductAtDate(BaseModel):
 | |
|     id: int
 | |
|     name: str
 | |
|     category_id: int
 | |
|     category: GroceryCategory
 | |
|     brand_id: Optional[int] = None
 | |
|     brand: Optional[Brand] = None
 | |
|     organic: bool = False
 | |
|     weight: Optional[float] = None
 | |
|     weight_unit: str = "g"
 | |
|     valid_from: date
 | |
|     valid_to: date
 | |
|     deleted: bool
 | |
|     was_current: bool  # True if from current table, False if from history
 | |
|     
 | |
|     class Config:
 | |
|         from_attributes = True
 | |
| 
 | |
| class ProductAtPurchase(BaseModel):
 | |
|     product: ProductAtDate
 | |
|     amount: float
 | |
|     price: float
 | |
|     discount: bool
 | |
|     
 | |
|     class Config:
 | |
|         from_attributes = True
 | |
| 
 | |
| # Shop schemas
 | |
| class ShopBase(BaseModel):
 | |
|     name: str
 | |
|     city: str
 | |
|     address: Optional[str] = None
 | |
| 
 | |
| class ShopCreate(ShopBase):
 | |
|     pass
 | |
| 
 | |
| class ShopUpdate(BaseModel):
 | |
|     name: Optional[str] = None
 | |
|     city: Optional[str] = None
 | |
|     address: Optional[str] = None
 | |
| 
 | |
| class Shop(ShopBase):
 | |
|     id: int
 | |
|     created_at: datetime
 | |
|     updated_at: Optional[datetime] = None
 | |
|     
 | |
|     class Config:
 | |
|         from_attributes = True
 | |
| 
 | |
| # Shopping Event schemas
 | |
| class ProductInEvent(BaseModel):
 | |
|     product_id: int
 | |
|     amount: float = Field(..., gt=0)
 | |
|     price: float = Field(..., ge=0)  # Price at the time of this shopping event (allow free items)
 | |
|     discount: bool = False  # Whether this product was purchased with a discount
 | |
| 
 | |
| class ProductWithEventData(BaseModel):
 | |
|     id: int
 | |
|     name: str
 | |
|     category: GroceryCategory
 | |
|     brand: Optional[Brand] = None
 | |
|     organic: bool
 | |
|     weight: Optional[float] = None
 | |
|     weight_unit: str
 | |
|     amount: float  # Amount purchased in this event
 | |
|     price: float   # Price at the time of this event
 | |
|     discount: bool  # Whether this product was purchased with a discount
 | |
|     
 | |
|     class Config:
 | |
|         from_attributes = True
 | |
| 
 | |
| class ShoppingEventBase(BaseModel):
 | |
|     shop_id: int
 | |
|     date: Optional[datetime] = None
 | |
|     total_amount: Optional[float] = Field(None, ge=0)
 | |
|     notes: Optional[str] = None
 | |
| 
 | |
| class ShoppingEventCreate(ShoppingEventBase):
 | |
|     products: List[ProductInEvent] = []
 | |
| 
 | |
| class ShoppingEventUpdate(BaseModel):
 | |
|     shop_id: Optional[int] = None
 | |
|     date: Optional[datetime] = None
 | |
|     total_amount: Optional[float] = Field(None, ge=0)
 | |
|     notes: Optional[str] = None
 | |
|     products: Optional[List[ProductInEvent]] = None
 | |
| 
 | |
| class ShoppingEventResponse(ShoppingEventBase):
 | |
|     id: int
 | |
|     created_at: datetime
 | |
|     updated_at: Optional[datetime] = None
 | |
|     shop: Shop
 | |
|     products: List[ProductWithEventData] = []
 | |
|     
 | |
|     class Config:
 | |
|         from_attributes = True
 | |
| 
 | |
| # Statistics schemas
 | |
| class CategoryStats(BaseModel):
 | |
|     category: str
 | |
|     total_spent: float
 | |
|     item_count: int
 | |
|     avg_price: float
 | |
| 
 | |
| class ShopStats(BaseModel):
 | |
|     shop_name: str
 | |
|     total_spent: float
 | |
|     visit_count: int
 | |
|     avg_per_visit: float
 | |
| 
 | |
| # Update forward references
 | |
| BrandInShop.model_rebuild()
 | |
| 
 | |
| # Related Products schemas
 | |
| class RelatedProductBase(BaseModel):
 | |
|     product_id: int
 | |
|     related_product_id: int
 | |
|     relationship_type: Optional[str] = None
 | |
| 
 | |
| class RelatedProductCreate(RelatedProductBase):
 | |
|     pass
 | |
| 
 | |
| class RelatedProductUpdate(BaseModel):
 | |
|     relationship_type: Optional[str] = None
 | |
| 
 | |
| class RelatedProduct(RelatedProductBase):
 | |
|     id: int
 | |
|     created_at: datetime
 | |
|     
 | |
|     class Config:
 | |
|         from_attributes = True
 | |
| 
 | |
| # Product with related products
 | |
| class ProductWithRelated(Product):
 | |
|     related_products: List["Product"] = []
 | |
|     
 | |
|     class Config:
 | |
|         from_attributes = True
 | |
| 
 | |
| # Update forward references for classes that reference other classes
 | |
| ProductWithRelated.model_rebuild()  |