Files
data-entry-app/backend/app/schemas/throughput.py
T
adminandClaude Opus 4.8 2de82776cb v0.1.19 - Throughput overview & responsive header
- Throughput: new "Throughput Overview" header with Gauge icon and brand-green
  badge icons on each card (Today, This week, 4-week average, Horse Mix, Grain Mix)
- Throughput: inline rolling-range selector (7d / 4w / 6w / 12w, default 4 weeks)
  driving the customer-mix cards; stats window widened to 12 weeks so switching
  range is a pure client-side re-filter
- Throughput: cards collapse to a single even 5-across row on laptop and up,
  with container-query value text that scales to each card's width
- Throughput: date logic pinned to Australian Eastern time (fixes the day-early
  date); This week subtitle shows the Mon-Sun date range
- Throughput: subtler tinted add-form; removed the inline-entry kicker and the
  "Open full form" link
- Topbar: fix cramped laptop header - action toggles no longer wrap above the
  user button; search drops to its own row earlier

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 10:01:10 +12:00

158 lines
5.3 KiB
Python

from __future__ import annotations
from datetime import date, datetime
from typing import Literal
from pydantic import BaseModel, ConfigDict, Field, field_validator
QuantityType = Literal["bags", "kg"]
class ThroughputProductBase(BaseModel):
model_config = ConfigDict(extra="forbid")
item_id: str | None = Field(default=None, max_length=64)
name: str = Field(min_length=1, max_length=255)
client_name: str | None = Field(default=None, max_length=255)
default_bag_size: float | None = Field(default=None, ge=0)
is_bulka_default: bool = False
active: bool = True
is_stock_item: bool = True
notes: str | None = Field(default=None, max_length=2000)
class ThroughputProductCreate(ThroughputProductBase):
pass
class ThroughputProductUpdate(BaseModel):
model_config = ConfigDict(extra="forbid")
item_id: str | None = Field(default=None, max_length=64)
name: str | None = Field(default=None, min_length=1, max_length=255)
client_name: str | None = Field(default=None, max_length=255)
default_bag_size: float | None = Field(default=None, ge=0)
is_bulka_default: bool | None = None
active: bool | None = None
is_stock_item: bool | None = None
notes: str | None = Field(default=None, max_length=2000)
class ThroughputProductRead(ThroughputProductBase):
id: int
tenant_id: str
created_at: datetime
updated_at: datetime
model_config = ConfigDict(from_attributes=True)
class ThroughputEntryBase(BaseModel):
model_config = ConfigDict(extra="forbid")
production_date: date
product_id: int | None = None
product_name_snapshot: str | None = Field(default=None, max_length=255)
bag_size: float | None = Field(default=None, ge=0)
scales_checked: bool = True
label_correct: bool = True
bag_sealed: bool = True
pallet_good_condition: bool = True
for_order: bool = False
for_stock: bool = False
job_number: str | None = Field(default=None, max_length=64)
stock_quantity: float | None = Field(default=None, ge=0)
sample_box_no: str | None = Field(default=None, max_length=64)
test_weight_1: float | None = Field(default=None, ge=0)
test_weight_2: float | None = Field(default=None, ge=0)
test_weight_3: float | None = Field(default=None, ge=0)
test_weight_4: float | None = Field(default=None, ge=0)
test_weight_5: float | None = Field(default=None, ge=0)
quantity: float = Field(ge=0)
quantity_type: QuantityType = "bags"
staff_name: str | None = Field(default=None, max_length=255)
notes: str | None = Field(default=None, max_length=2000)
@field_validator("job_number")
@classmethod
def _normalize_job_number(cls, value: str | None) -> str | None:
if value is None:
return None
stripped = value.strip()
return stripped or None
@field_validator("staff_name")
@classmethod
def _normalize_staff(cls, value: str | None) -> str | None:
if value is None:
return None
stripped = value.strip()
return stripped or None
class ThroughputEntryCreate(ThroughputEntryBase):
pass
class ThroughputEntryUpdate(BaseModel):
model_config = ConfigDict(extra="forbid")
production_date: date | None = None
product_id: int | None = None
product_name_snapshot: str | None = Field(default=None, max_length=255)
bag_size: float | None = Field(default=None, ge=0)
scales_checked: bool | None = None
label_correct: bool | None = None
bag_sealed: bool | None = None
pallet_good_condition: bool | None = None
for_order: bool | None = None
for_stock: bool | None = None
job_number: str | None = Field(default=None, max_length=64)
stock_quantity: float | None = Field(default=None, ge=0)
sample_box_no: str | None = Field(default=None, max_length=64)
test_weight_1: float | None = Field(default=None, ge=0)
test_weight_2: float | None = Field(default=None, ge=0)
test_weight_3: float | None = Field(default=None, ge=0)
test_weight_4: float | None = Field(default=None, ge=0)
test_weight_5: float | None = Field(default=None, ge=0)
quantity: float | None = Field(default=None, ge=0)
quantity_type: QuantityType | None = None
staff_name: str | None = Field(default=None, max_length=255)
notes: str | None = Field(default=None, max_length=2000)
class ThroughputImportResult(BaseModel):
entries_imported: int
entries_skipped: int
products_created: int
errors: list[str] = Field(default_factory=list)
class ThroughputEntryRead(BaseModel):
id: int
tenant_id: str
production_date: date
product_id: int | None
product_name_snapshot: str
bag_size: float | None
scales_checked: bool
label_correct: bool
bag_sealed: bool
pallet_good_condition: bool
for_order: bool
for_stock: bool
job_number: str | None
stock_quantity: float | None
sample_box_no: str | None
test_weight_1: float | None
test_weight_2: float | None
test_weight_3: float | None
test_weight_4: float | None
test_weight_5: float | None
quantity: float
quantity_type: QuantityType
calculated_kg: float
staff_name: str | None
notes: str | None
qa_passed: bool
created_by: str | None
created_at: datetime
updated_at: datetime
model_config = ConfigDict(from_attributes=True)