aa9ac2a6ea
- Migration 006 : colonne tags TEXT[] sur shopping.products - Modèle SQLAlchemy + schémas Pydantic mis à jour (ProductCreate/Update/Response) - Interface TypeScript Product/ProductCreate/ProductUpdate avec tags?: string[] - CatalogueModal : chip input (Entrée/virgule pour ajouter, clic pour supprimer, Backspace pour retirer le dernier) - Recherche dans le catalogue et le bottom sheet étendue aux tags (insensible aux accents) - Tags affichés en pills dans la liste du catalogue v0.4.12 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
150 lines
3.8 KiB
Python
150 lines
3.8 KiB
Python
import uuid
|
|
from datetime import datetime, date
|
|
from decimal import Decimal
|
|
from typing import Literal
|
|
from pydantic import BaseModel, ConfigDict, model_validator
|
|
|
|
|
|
class StoreCreate(BaseModel):
|
|
name: str
|
|
location: str | None = None
|
|
url: str | None = None
|
|
store_type: str | None = None
|
|
|
|
|
|
class StoreUpdate(BaseModel):
|
|
name: str | None = None
|
|
location: str | None = None
|
|
url: str | None = None
|
|
store_type: str | None = None
|
|
|
|
|
|
class StoreResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: uuid.UUID
|
|
name: str
|
|
location: str | None
|
|
url: str | None
|
|
store_type: str | None
|
|
|
|
|
|
class ProductCreate(BaseModel):
|
|
name: str
|
|
brand: str | None = None
|
|
category: str | None = None
|
|
description: str | None = None
|
|
default_unit: str | None = None
|
|
barcode: str | None = None
|
|
price: Decimal | None = None
|
|
quantity_per_unit: Decimal | None = None
|
|
default_store_id: uuid.UUID | None = None
|
|
image_path: str | None = None
|
|
thumbnail_path: str | None = None
|
|
tags: list[str] | None = None
|
|
|
|
|
|
class ProductUpdate(BaseModel):
|
|
name: str | None = None
|
|
brand: str | None = None
|
|
category: str | None = None
|
|
description: str | None = None
|
|
default_unit: str | None = None
|
|
barcode: str | None = None
|
|
price: Decimal | None = None
|
|
quantity_per_unit: Decimal | None = None
|
|
default_store_id: uuid.UUID | None = None
|
|
image_path: str | None = None
|
|
thumbnail_path: str | None = None
|
|
tags: list[str] | None = None
|
|
|
|
|
|
class ProductResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: uuid.UUID
|
|
name: str
|
|
brand: str | None
|
|
category: str | None
|
|
description: str | None
|
|
default_unit: str | None
|
|
barcode: str | None
|
|
price: Decimal | None
|
|
quantity_per_unit: Decimal | None
|
|
default_store_id: uuid.UUID | None
|
|
frequency_score: int
|
|
last_purchased_at: date | None
|
|
avg_interval_days: Decimal | None
|
|
image_path: str | None
|
|
thumbnail_path: str | None
|
|
tags: list[str] | None
|
|
|
|
|
|
class ListItemCreate(BaseModel):
|
|
product_id: uuid.UUID | None = None
|
|
custom_name: str | None = None
|
|
quantity: Decimal | None = None
|
|
unit: str | None = None
|
|
|
|
@model_validator(mode='after')
|
|
def must_have_name(self) -> 'ListItemCreate':
|
|
if not self.product_id and not self.custom_name:
|
|
raise ValueError('product_id ou custom_name requis')
|
|
return self
|
|
|
|
|
|
class ListItemUpdate(BaseModel):
|
|
is_checked: bool | None = None
|
|
quantity: Decimal | None = None
|
|
unit: str | None = None
|
|
price_recorded: Decimal | None = None
|
|
|
|
|
|
class ListItemResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: uuid.UUID
|
|
product_id: uuid.UUID | None
|
|
custom_name: str | None
|
|
display_name: str
|
|
quantity: Decimal | None
|
|
unit: str | None
|
|
is_checked: bool
|
|
price_recorded: Decimal | None
|
|
carried_over: bool
|
|
sort_order: int | None
|
|
|
|
|
|
class ShoppingListCreate(BaseModel):
|
|
name: str | None = None
|
|
store_id: uuid.UUID | None = None
|
|
week_date: date | None = None
|
|
|
|
|
|
class ShoppingListUpdate(BaseModel):
|
|
name: str | None = None
|
|
store_id: uuid.UUID | None = None
|
|
status: Literal['draft', 'active', 'done'] | None = None
|
|
|
|
|
|
class ShoppingListResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: uuid.UUID
|
|
name: str | None
|
|
store_id: uuid.UUID | None
|
|
week_date: date | None
|
|
status: str
|
|
created_at: datetime
|
|
item_count: int
|
|
checked_count: int
|
|
|
|
|
|
class ShoppingListDetailResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: uuid.UUID
|
|
name: str | None
|
|
store_id: uuid.UUID | None
|
|
week_date: date | None
|
|
status: str
|
|
created_at: datetime
|
|
item_count: int
|
|
checked_count: int
|
|
items: list[ListItemResponse]
|