- Mix Calculator: searchable Mix Name picker (mirrors Throughput search) - Ingredients Editor: add manual Category column; used to order Mix Calculator output - Mix Calculator: surface formula-only mixes (no product yet) via -mix_id sentinel - Throughput: remove unused For order / For stock destination controls from composer - Editor change history: show timestamps in local time (stored UTC) instead of raw UTC Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
109 lines
2.8 KiB
Python
109 lines
2.8 KiB
Python
from datetime import date, datetime
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
|
|
|
|
class MixCalculatorProductOptionRead(BaseModel):
|
|
product_id: int
|
|
client_name: str
|
|
product_name: str
|
|
mix_id: int
|
|
mix_name: str
|
|
unit_of_measure: str
|
|
unit_size_kg: float
|
|
mix_total_kg: float
|
|
|
|
|
|
class MixCalculatorOptionsRead(BaseModel):
|
|
clients: list[str]
|
|
products: list[MixCalculatorProductOptionRead]
|
|
|
|
|
|
class MixCalculatorSessionLineRead(BaseModel):
|
|
id: int | None = None
|
|
raw_material_id: int | None
|
|
raw_material_name: str
|
|
required_kg: float
|
|
mix_percentage: float
|
|
unit: str
|
|
rounding_decimals: int = 2
|
|
# Manual ingredient grouping used to order the calculator output.
|
|
category: str | None = None
|
|
sort_order: int
|
|
|
|
|
|
class MixCalculatorSessionBase(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
mix_date: date
|
|
client_name: str = Field(min_length=1, max_length=255)
|
|
product_id: int
|
|
batch_size_kg: float = Field(gt=0)
|
|
prepared_by_name: str = Field(min_length=1, max_length=255)
|
|
status: str = "saved"
|
|
notes: str | None = Field(default=None, max_length=2000)
|
|
|
|
|
|
class MixCalculatorSessionCreate(MixCalculatorSessionBase):
|
|
pass
|
|
|
|
|
|
class MixCalculatorSessionUpdate(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
mix_date: date | None = None
|
|
client_name: str | None = Field(default=None, min_length=1, max_length=255)
|
|
product_id: int | None = None
|
|
batch_size_kg: float | None = Field(default=None, gt=0)
|
|
prepared_by_name: str | None = Field(default=None, min_length=1, max_length=255)
|
|
status: str | None = None
|
|
notes: str | None = Field(default=None, max_length=2000)
|
|
|
|
|
|
class MixCalculatorPreviewRead(BaseModel):
|
|
client_name: str
|
|
product_id: int
|
|
product_name: str
|
|
mix_id: int
|
|
mix_name: str
|
|
mix_date: date
|
|
batch_size_kg: float
|
|
total_bags: float
|
|
total_kg: float
|
|
product_unit_of_measure: str
|
|
product_unit_size_kg: float
|
|
prepared_by_name: str
|
|
status: str
|
|
notes: str | None
|
|
warnings: list[str]
|
|
lines: list[MixCalculatorSessionLineRead]
|
|
|
|
|
|
class MixCalculatorSessionSummaryRead(BaseModel):
|
|
id: int
|
|
tenant_id: str
|
|
session_number: str
|
|
client_name: str
|
|
product_id: int
|
|
product_name: str
|
|
mix_id: int
|
|
mix_name: str
|
|
mix_date: date
|
|
batch_size_kg: float
|
|
total_bags: float
|
|
total_kg: float
|
|
product_unit_of_measure: str
|
|
product_unit_size_kg: float
|
|
prepared_by_user_id: int | None
|
|
prepared_by_name: str
|
|
created_by: str
|
|
status: str
|
|
notes: str | None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
warnings: list[str]
|
|
is_owner: bool
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class MixCalculatorSessionRead(MixCalculatorSessionSummaryRead):
|
|
lines: list[MixCalculatorSessionLineRead]
|