""" Pydantic schemas for BillableTime model. Request and response schemas for billable time entries with billing information. """ from datetime import datetime from typing import Optional from uuid import UUID from pydantic import BaseModel, Field, field_validator class BillableTimeBase(BaseModel): """Base schema with shared BillableTime fields.""" work_item_id: Optional[str] = Field(None, description="Foreign key to work_items table (UUID)") session_id: Optional[str] = Field(None, description="Foreign key to sessions table (UUID)") client_id: str = Field(..., description="Foreign key to clients table (UUID)") start_time: datetime = Field(..., description="When the billable time started") end_time: Optional[datetime] = Field(None, description="When the billable time ended") duration_minutes: int = Field(..., description="Duration in minutes (auto-calculated or manual)", gt=0) hourly_rate: float = Field(..., description="Hourly rate applied to this time entry", ge=0) total_amount: float = Field(..., description="Total billable amount (calculated)", ge=0) is_billable: bool = Field(True, description="Whether this time entry is actually billable") description: str = Field(..., description="Description of the work performed") category: str = Field(..., description="Category: consulting, development, support, maintenance, troubleshooting, project_work, training, documentation") notes: Optional[str] = Field(None, description="Additional notes about this time entry") invoiced_at: Optional[datetime] = Field(None, description="When this time entry was invoiced") invoice_id: Optional[str] = Field(None, description="Reference to invoice if applicable") @field_validator('category') @classmethod def validate_category(cls, v: str) -> str: """Validate that category is one of the allowed values.""" allowed_categories = { 'consulting', 'development', 'support', 'maintenance', 'troubleshooting', 'project_work', 'training', 'documentation' } if v not in allowed_categories: raise ValueError(f"Category must be one of: {', '.join(allowed_categories)}") return v @field_validator('end_time') @classmethod def validate_end_time(cls, v: Optional[datetime], info) -> Optional[datetime]: """Validate that end_time is after start_time if provided.""" if v is not None and 'start_time' in info.data: start_time = info.data['start_time'] if v < start_time: raise ValueError("end_time must be after start_time") return v class BillableTimeCreate(BillableTimeBase): """Schema for creating a new BillableTime entry.""" pass class BillableTimeUpdate(BaseModel): """Schema for updating an existing BillableTime entry. All fields are optional.""" work_item_id: Optional[str] = Field(None, description="Foreign key to work_items table (UUID)") session_id: Optional[str] = Field(None, description="Foreign key to sessions table (UUID)") client_id: Optional[str] = Field(None, description="Foreign key to clients table (UUID)") start_time: Optional[datetime] = Field(None, description="When the billable time started") end_time: Optional[datetime] = Field(None, description="When the billable time ended") duration_minutes: Optional[int] = Field(None, description="Duration in minutes (auto-calculated or manual)", gt=0) hourly_rate: Optional[float] = Field(None, description="Hourly rate applied to this time entry", ge=0) total_amount: Optional[float] = Field(None, description="Total billable amount (calculated)", ge=0) is_billable: Optional[bool] = Field(None, description="Whether this time entry is actually billable") description: Optional[str] = Field(None, description="Description of the work performed") category: Optional[str] = Field(None, description="Category: consulting, development, support, maintenance, troubleshooting, project_work, training, documentation") notes: Optional[str] = Field(None, description="Additional notes about this time entry") invoiced_at: Optional[datetime] = Field(None, description="When this time entry was invoiced") invoice_id: Optional[str] = Field(None, description="Reference to invoice if applicable") @field_validator('category') @classmethod def validate_category(cls, v: Optional[str]) -> Optional[str]: """Validate that category is one of the allowed values.""" if v is not None: allowed_categories = { 'consulting', 'development', 'support', 'maintenance', 'troubleshooting', 'project_work', 'training', 'documentation' } if v not in allowed_categories: raise ValueError(f"Category must be one of: {', '.join(allowed_categories)}") return v class BillableTimeResponse(BillableTimeBase): """Schema for BillableTime responses with ID and timestamps.""" id: UUID = Field(..., description="Unique identifier for the billable time entry") created_at: datetime = Field(..., description="Timestamp when the entry was created") updated_at: datetime = Field(..., description="Timestamp when the entry was last updated") model_config = {"from_attributes": True}