from pydantic import BaseModel, Field from datetime import datetime from typing import Optional, List, Dict, Any from enum import Enum class UserRole(str, Enum): admin = "admin" user = "user" class IssueStatus(str, Enum): new = "new" progress = "progress" complete = "complete" class IssueCategory(str, Enum): material_missing = "material_missing" design_error = "design_error" # 설계미스 (기존 dimension_defect 대체) incoming_defect = "incoming_defect" inspection_miss = "inspection_miss" # 검사미스 (신규 추가) etc = "etc" # 기타 class ReviewStatus(str, Enum): pending_review = "pending_review" # 수신함 (검토 대기) in_progress = "in_progress" # 관리함 (진행 중) completed = "completed" # 관리함 (완료됨) disposed = "disposed" # 폐기함 (폐기됨) class DisposalReasonType(str, Enum): duplicate = "duplicate" # 중복 (기본값) invalid_report = "invalid_report" # 잘못된 신고 not_applicable = "not_applicable" # 해당 없음 spam = "spam" # 스팸/오류 custom = "custom" # 직접 입력 # User schemas class UserBase(BaseModel): username: str full_name: Optional[str] = None role: UserRole = UserRole.user class UserCreate(UserBase): password: str class UserUpdate(BaseModel): full_name: Optional[str] = None password: Optional[str] = None role: Optional[UserRole] = None is_active: Optional[bool] = None class PasswordChange(BaseModel): current_password: str new_password: str class User(UserBase): id: int is_active: bool created_at: datetime class Config: from_attributes = True # Auth schemas class Token(BaseModel): access_token: str token_type: str user: User class TokenData(BaseModel): username: Optional[str] = None class LoginRequest(BaseModel): username: str password: str # Issue schemas class IssueBase(BaseModel): category: IssueCategory description: str project_id: int class IssueCreate(IssueBase): photo: Optional[str] = None # Base64 encoded image photo2: Optional[str] = None # Second Base64 encoded image class IssueUpdate(BaseModel): category: Optional[IssueCategory] = None description: Optional[str] = None project_id: Optional[int] = None work_hours: Optional[float] = None detail_notes: Optional[str] = None status: Optional[IssueStatus] = None photo: Optional[str] = None # Base64 encoded image for update photo2: Optional[str] = None # Second Base64 encoded image for update class Issue(IssueBase): id: int photo_path: Optional[str] = None photo_path2: Optional[str] = None # 두 번째 사진 경로 status: IssueStatus reporter_id: int reporter: User project_id: Optional[int] = None # project: Optional['Project'] = None # 순환 참조 방지를 위해 제거 report_date: datetime work_hours: float detail_notes: Optional[str] = None # 수신함 워크플로우 관련 필드들 review_status: ReviewStatus disposal_reason: Optional[DisposalReasonType] = None custom_disposal_reason: Optional[str] = None disposed_at: Optional[datetime] = None reviewed_by_id: Optional[int] = None reviewed_at: Optional[datetime] = None original_data: Optional[Dict[str, Any]] = None modification_log: Optional[List[Dict[str, Any]]] = None class Config: from_attributes = True # 수신함 워크플로우 전용 스키마들 class IssueDisposalRequest(BaseModel): """부적합 폐기 요청""" disposal_reason: DisposalReasonType = DisposalReasonType.duplicate custom_disposal_reason: Optional[str] = None class IssueReviewRequest(BaseModel): """부적합 검토 및 수정 요청""" project_id: Optional[int] = None category: Optional[IssueCategory] = None description: Optional[str] = None modifications: Optional[Dict[str, Any]] = None class IssueStatusUpdateRequest(BaseModel): """부적합 상태 변경 요청""" review_status: ReviewStatus notes: Optional[str] = None class InboxIssue(BaseModel): """수신함용 부적합 정보 (간소화된 버전)""" id: int category: IssueCategory description: str photo_path: Optional[str] = None photo_path2: Optional[str] = None project_id: Optional[int] = None reporter_id: int reporter: User report_date: datetime review_status: ReviewStatus class Config: from_attributes = True class ModificationLogEntry(BaseModel): """수정 이력 항목""" field: str old_value: Any new_value: Any modified_at: datetime modified_by: int # Project schemas class ProjectBase(BaseModel): job_no: str = Field(..., min_length=1, max_length=50) project_name: str = Field(..., min_length=1, max_length=200) class ProjectCreate(ProjectBase): pass class ProjectUpdate(BaseModel): project_name: Optional[str] = Field(None, min_length=1, max_length=200) is_active: Optional[bool] = None class Project(ProjectBase): id: int created_by_id: int created_by: User created_at: datetime is_active: bool # issues: Optional[List['Issue']] = None # 순환 참조 방지를 위해 제거 class Config: from_attributes = True # Daily Work schemas class DailyWorkBase(BaseModel): date: datetime worker_count: int = Field(gt=0) overtime_workers: Optional[int] = 0 overtime_hours: Optional[float] = 0 class DailyWorkCreate(DailyWorkBase): pass class DailyWorkUpdate(BaseModel): worker_count: Optional[int] = Field(None, gt=0) overtime_workers: Optional[int] = None overtime_hours: Optional[float] = None class DailyWork(DailyWorkBase): id: int regular_hours: float overtime_total: float total_hours: float created_by_id: int created_by: User created_at: datetime class Config: from_attributes = True # Report schemas class ReportRequest(BaseModel): start_date: datetime end_date: datetime class CategoryStats(BaseModel): material_missing: int = 0 dimension_defect: int = 0 incoming_defect: int = 0 class ReportSummary(BaseModel): start_date: datetime end_date: datetime total_hours: float total_issues: int category_stats: CategoryStats completed_issues: int average_resolution_time: float # Project Daily Work schemas class ProjectDailyWorkBase(BaseModel): date: datetime project_id: int hours: float class ProjectDailyWorkCreate(ProjectDailyWorkBase): pass class ProjectDailyWork(ProjectDailyWorkBase): id: int created_by_id: int created_at: datetime project: Project class Config: from_attributes = True