from pydantic import Field, BaseModel,field_validator from fastapi import HTTPException from ..common.datetime import get_current_date from typing import List, Optional, Literal from ..common.validators import DateStr from ..common.base import BaseResponseWithID from ..common.enums import ApprovalStatus from datetime import datetime class MaterialDetailResponse(BaseResponseWithID): """ 物资明细模型,存储在CouchDB中的物资明细文档 """ project_id: str = Field(..., description="所属项目ID", examples=["project123"]) parent_id: str = Field(..., description="所属物资ID", examples=["material789"]) date: DateStr = Field(..., description="实际到货日期", examples=["2024-05-31"]) rate: Optional[float] = Field(None, description="实际到货率(%)", examples=[0.0]) quantity: float = Field(0.0, ge=0, description="实际到货量累计", examples=[0.0]) status: Optional[int] = Field(None, description="状态 0:正常 1:超前 -1:延误", examples=[0]) remark: Optional[str] = Field(None, description="填报备注", examples=[""]) is_complete: Optional[bool] = Field( None, description="是否为全部到货", examples=[False] ) class MaterialDetailCreate(BaseModel): """ 物资明细创建模型 MATERIAL_DETAIL = "material.detail" """ project_id: str = Field(..., description="所属项目ID", examples=["project123"]) parent_id: str = Field(..., description="所属物资ID", examples=["material789"]) quantity: float = Field(0.0, ge=0, description="实际到货量累计", examples=[0.0]) date: DateStr = Field(..., description="实际到货日期", examples=["2024-05-31"]) remark: Optional[str] = Field(None, description="填报备注", examples=[""]) is_complete: Optional[bool] = Field( None, description="是否为全部到货", examples=[False] ) confirmation_time: str | None = Field(None, description="确认时间", examples=["2025-12-17 11:13:49"]) approval_time: str | None = Field(None, description="审批时间", examples=["2025-12-17 11:13:49"]) approval_status: ApprovalStatus = Field("registered", description="审批状态", examples=["registered"]) @field_validator("approval_status") def validate_approval_status(cls, v): """验证审批字段""" try: return ApprovalStatus(v) # 尝试转换 except ValueError: raise HTTPException(400, f"审批状态 '{v}' 无效") @field_validator("confirmation_time", "approval_time") def validate_time(cls, v): """验证时间""" if v is None: return v try: datetime.strptime(v, "%Y-%m-%d %H:%M:%S") except ValueError: raise HTTPException(detail=f'{v}时间格式错误,应为:2025-12-17 11:13:49', status_code=400) return v @field_validator("quantity") def validate_quantity(cls, v): """验证实际完成量累计必须为非负数""" if v < 0: raise HTTPException(status_code=400, detail='实际完成量累计不能小于0') return v @field_validator("date") def validate_date(cls, v): if v > get_current_date(): raise HTTPException(detail='节点日期不能大于今天', status_code=400) return v class MaterialDetailUpdate(BaseModel): """ 物资明细更新模型 MATERIAL_DETAIL = "material.detail" """ id: str = Field(..., description="物资明细ID") quantity: Optional[float] = Field(None, ge=0, description="实际到货量累计", examples=[0.0]) date: Optional[DateStr] = Field(None, description="实际到货日期", examples=["2024-05-31"]) remark: Optional[str] = Field(None, description="填报备注", examples=[""]) is_complete: Optional[bool] = Field( None, description="是否为全部到货", examples=[False] ) confirmation_time: str | None = Field(None, description="确认时间", examples=["2025-12-17 11:13:49"]) approval_time: str | None = Field(None, description="审批时间", examples=["2025-12-17 11:13:49"]) approval_status: ApprovalStatus | None = Field("registered", description="审批状态", examples=["registered"]) @field_validator("approval_status") def validate_approval_status(cls, v): """验证审批字段""" if v is None: return v try: return ApprovalStatus(v) # 尝试转换 except ValueError: raise HTTPException(400, f"审批状态 '{v}' 无效") @field_validator("confirmation_time", "approval_time") def validate_time(cls, v): """验证时间""" if v is None: return v try: datetime.strptime(v, "%Y-%m-%d %H:%M:%S") except ValueError: raise HTTPException(detail=f'{v}时间格式错误,应为:2025-12-17 11:13:49', status_code=400) return v @field_validator("quantity") def validate_quantity(cls, v): """验证实际完成量累计必须为非负数""" if v is None: return None if v < 0: raise HTTPException(status_code=400, detail='实际完成量累计不能小于0') return v @field_validator("date") def validate_date(cls, v): if v is None: return None if v > get_current_date(): raise HTTPException(detail='节点日期不能大于今天', status_code=400) return v class MaterialDetailBulkUpdate(BaseModel): detail_list: List[MaterialDetailUpdate]