feat(T06): Verkaufsmodul + Rechtsdokumente + DATEV-Export + Sales UI
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
"""Pydantic schemas for DATEV export request and response bodies."""
|
||||
|
||||
import uuid
|
||||
from datetime import date, datetime
|
||||
from decimal import Decimal
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
|
||||
class DATEVExportCreate(BaseModel):
|
||||
"""POST /api/v1/datev/export request body."""
|
||||
|
||||
start_date: date = Field(..., description="Start date of the export range")
|
||||
end_date: date = Field(..., description="End date of the export range (inclusive)")
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_date_range(self) -> "DATEVExportCreate":
|
||||
"""Ensure start_date is not after end_date."""
|
||||
if self.start_date > self.end_date:
|
||||
raise ValueError("start_date must not be after end_date")
|
||||
return self
|
||||
|
||||
|
||||
class DATEVExportResponse(BaseModel):
|
||||
"""DATEV export response schema."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: uuid.UUID
|
||||
start_date: date
|
||||
end_date: date
|
||||
file_path: Optional[str] = None
|
||||
total_amount: Decimal
|
||||
created_at: Optional[datetime] = None
|
||||
|
||||
|
||||
class DATEVExportListResponse(BaseModel):
|
||||
"""Paginated DATEV export list response."""
|
||||
|
||||
items: list[DATEVExportResponse]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
@@ -0,0 +1,107 @@
|
||||
"""Pydantic schemas for sale-related request and response bodies."""
|
||||
|
||||
import uuid
|
||||
from datetime import date, datetime
|
||||
from decimal import Decimal
|
||||
from typing import Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
SALE_STATUSES = Literal["draft", "completed", "cancelled"]
|
||||
|
||||
|
||||
class SaleCreate(BaseModel):
|
||||
"""POST /api/v1/sales request body."""
|
||||
|
||||
vehicle_id: uuid.UUID
|
||||
buyer_contact_id: uuid.UUID
|
||||
seller_contact_id: Optional[uuid.UUID] = None
|
||||
sale_price: Decimal = Field(..., ge=0)
|
||||
sale_date: Optional[date] = None
|
||||
status: SALE_STATUSES = "draft"
|
||||
is_gwg: bool = False
|
||||
|
||||
|
||||
class SaleUpdate(BaseModel):
|
||||
"""PUT /api/v1/sales/:id request body (all fields optional)."""
|
||||
|
||||
sale_price: Optional[Decimal] = Field(None, ge=0)
|
||||
sale_date: Optional[date] = None
|
||||
status: Optional[SALE_STATUSES] = None
|
||||
is_gwg: Optional[bool] = None
|
||||
seller_contact_id: Optional[uuid.UUID] = None
|
||||
|
||||
|
||||
class VehicleNested(BaseModel):
|
||||
"""Nested vehicle info in sale response."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: uuid.UUID
|
||||
make: str
|
||||
model: str
|
||||
fin: str
|
||||
vehicle_type: str
|
||||
availability: str
|
||||
price: Decimal
|
||||
|
||||
|
||||
class ContactNested(BaseModel):
|
||||
"""Nested contact info in sale response."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: uuid.UUID
|
||||
company_name: str
|
||||
address_city: Optional[str] = None
|
||||
address_country: str
|
||||
vat_id: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
|
||||
|
||||
class SaleResponse(BaseModel):
|
||||
"""Sale response schema with nested vehicle and contacts."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: uuid.UUID
|
||||
vehicle_id: uuid.UUID
|
||||
buyer_contact_id: uuid.UUID
|
||||
seller_contact_id: Optional[uuid.UUID] = None
|
||||
sale_price: Decimal
|
||||
sale_date: date
|
||||
status: str
|
||||
is_gwg: bool
|
||||
contract_pdf_path: Optional[str] = None
|
||||
created_at: Optional[datetime] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
vehicle: Optional[VehicleNested] = None
|
||||
buyer: Optional[ContactNested] = None
|
||||
seller: Optional[ContactNested] = None
|
||||
|
||||
|
||||
class SaleListResponse(BaseModel):
|
||||
"""Paginated sale list response."""
|
||||
|
||||
items: list[SaleResponse]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
|
||||
|
||||
class ContractResponse(BaseModel):
|
||||
"""Response for contract PDF generation/download."""
|
||||
|
||||
sale_id: uuid.UUID
|
||||
contract_pdf_path: str
|
||||
message: str = "Contract generated"
|
||||
|
||||
|
||||
class UstIdVerifyResponse(BaseModel):
|
||||
"""Response for USt-IdNr. verification."""
|
||||
|
||||
verified: bool
|
||||
message: str
|
||||
vat_id: Optional[str] = None
|
||||
Reference in New Issue
Block a user