Olympus/backend/hermes/app/schemas/repair.py
2026-07-04 11:46:58 +02:00

162 lines
4.4 KiB
Python

from datetime import datetime
from typing import Literal
from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator, model_validator
RepairStatus = Literal[
"new",
"accepted",
"diagnosis",
"estimate",
"waiting_for_customer",
"approved",
"repair",
"final_test",
"ready_for_pickup",
"shipped",
"completed",
"cancelled",
]
RepairPriority = Literal["low", "normal", "high", "urgent"]
RepairSource = Literal["manual", "website", "customer_portal", "email", "phone"]
def normalize_text(value: object) -> str:
if value is None:
return ""
return str(value).strip()
class RepairBase(BaseModel):
customer_id: int | None = None
contact_id: int | None = None
status: RepairStatus = "new"
priority: RepairPriority = "normal"
source: RepairSource = "manual"
source_reference: str | None = Field(default=None, max_length=120)
customer_name: str = Field(default="", max_length=255)
customer_email: EmailStr | None = None
customer_phone: str = Field(default="", max_length=80)
device_manufacturer: str = Field(min_length=1, max_length=160)
device_model: str = Field(min_length=1, max_length=160)
device_serial_number: str | None = Field(default=None, max_length=160)
device_type: str = Field(default="", max_length=120)
accessories: str = ""
fault_description: str = Field(min_length=1)
previous_work: str = ""
device_opened: bool = False
intake_notes: str = ""
diagnosis_notes: str = ""
repair_notes: str = ""
estimate_notes: str = ""
estimated_cost_cents: int | None = Field(default=None, ge=0)
@field_validator(
"source_reference",
"customer_name",
"customer_phone",
"device_manufacturer",
"device_model",
"device_serial_number",
"device_type",
"accessories",
"fault_description",
"previous_work",
"intake_notes",
"diagnosis_notes",
"repair_notes",
"estimate_notes",
mode="before",
)
@classmethod
def normalize_strings(cls, value: object) -> str | None:
if value is None:
return None
return normalize_text(value)
@model_validator(mode="after")
def validate_customer_identity(self):
if self.customer_id is None and not self.customer_name:
raise ValueError("Kundenname ist erforderlich, wenn kein Kunde ausgewählt ist")
return self
class RepairCreate(RepairBase):
pass
class RepairUpdate(RepairBase):
pass
class RepairStatusUpdate(BaseModel):
status: RepairStatus
note: str | None = None
@field_validator("note", mode="before")
@classmethod
def normalize_note(cls, value: object) -> str | None:
if value is None:
return None
text = normalize_text(value)
return text or None
class RepairResponse(RepairBase):
id: int
repair_number: str
customer_email: str
device_serial_number: str | None
approved_at: datetime | None
completed_at: datetime | None
created_at: datetime
updated_at: datetime
model_config = ConfigDict(from_attributes=True)
class RepairListResponse(BaseModel):
items: list[RepairResponse]
total: int
limit: int
offset: int
class RepairStatusHistoryResponse(BaseModel):
id: int
repair_id: int
old_status: str | None
new_status: str
note: str | None
actor_user_id: int | None
created_at: datetime
model_config = ConfigDict(from_attributes=True)
class RepairIntakePayload(BaseModel):
name: str = Field(min_length=1, max_length=255)
email: EmailStr | None = None
phone: str = Field(default="", max_length=80)
manufacturer: str = Field(min_length=1, max_length=160)
model: str = Field(min_length=1, max_length=160)
serial_number: str | None = Field(default=None, max_length=160)
device_type: str = Field(default="", max_length=120)
accessories: str = ""
fault_description: str = Field(min_length=1)
previous_work: str = ""
device_opened: bool = False
source_reference: str | None = Field(default=None, max_length=120)
@field_validator("*", mode="before")
@classmethod
def normalize_payload(cls, value: object) -> object:
if isinstance(value, str):
return value.strip()
return value
class RepairIntakeResponse(BaseModel):
repair_number: str
status: RepairStatus
message: str