dodil/erp-brick-pos
DODIL ERP brick: point of sale
src/brick_pos/application/inputs.py
"""The bounded inputs of this brick's use cases. An amount travels as a string and is checked INSIDE the use case (after the
permission), so a caller sees 422 {gate: bounds} and the reason, never an engine 500; ids and words are bounded here."""
from typing import Annotated, Any, Optional
from erp_platform.types import text
from pydantic import BaseModel, ConfigDict, Field, WithJsonSchema
Code = text(64, min_length=1)
Reason = text(500)
Word = text(200)
# Typed `Any` on purpose: pydantic would quietly make `true` the number 1 and a float a rounded guess; the use case sees what the
# caller sent and answers 422 {gate: bounds} for anything but an exact decimal string (domain/money.py).
Amt = Annotated[Any, WithJsonSchema({"type": "string", "pattern": r"^[0-9]{1,16}(\.[0-9]{1,3})?$",
"description": "an exact decimal amount, as a string (\"10.00\")"})]
Setting = Annotated[Any, WithJsonSchema({"description": "true or false, an exact decimal string (\"5.00\"), a time (\"04:00\"), "
"a time zone (\"Europe/London\") or a word, as the setting's kind says"})]
class _In(BaseModel):
model_config = ConfigDict(extra="forbid")
class LineIn(_In):
"""One product rung up. The price and the VAT come from product and tax, never from the till."""
product_id: Code
quantity: Amt
unit_code: Optional[Code] = None
modifiers: Optional[list[Code]] = None # modifier products (MOD-OAT) that change the recipe the sale consumes, never the price
class RefundLineIn(_In):
"""One line of the original sale given back: its number on the sale and the quantity returned. `restock`: the item goes back on the
shelf (only where its category allows it; omitted, the category decides)."""
line_no: Annotated[int, Field(ge=1, le=1000)]
quantity: Amt
restock: Optional[bool] = None
class VoucherSaleIn(_In):
"""A gift voucher sold on this receipt: its code and its face value. It is live once the receipt is final."""
voucher_code: Code
amount: Amt
class RecipeLineIn(_In):
"""One component of a recipe: a product in the unit it is issued in (0.018 KGM of beans, 1 C62 of cups)."""
component_id: Code
quantity: Amt
unit_code: Code
class RecipeModifierIn(_In):
"""A modifier's component: it ADDS the component (replaces left out) or takes the place of the base component it replaces."""
modifier_id: Code
component_id: Code
quantity: Amt
unit_code: Code
replaces: Optional[Code] = None