from sqlalchemy import Column, Integer, BigInteger, String, DateTime, Float, Boolean, Text, ForeignKey, Enum, Index from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship from datetime import datetime, timezone, timedelta import enum # 한국 시간대 설정 KST = timezone(timedelta(hours=9)) def get_kst_now(): """현재 한국 시간 반환""" return datetime.now(KST) Base = declarative_base() class UserRole(str, enum.Enum): admin = "admin" # 관리자 user = "user" # 일반 사용자 class IssueStatus(str, enum.Enum): new = "new" progress = "progress" complete = "complete" class IssueCategory(str, enum.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.Enum): pending_review = "pending_review" # 수신함 (검토 대기) in_progress = "in_progress" # 관리함 (진행 중) completed = "completed" # 관리함 (완료됨) disposed = "disposed" # 폐기함 (폐기됨) class DisposalReasonType(str, enum.Enum): duplicate = "duplicate" # 중복 (기본값) invalid_report = "invalid_report" # 잘못된 신고 not_applicable = "not_applicable" # 해당 없음 spam = "spam" # 스팸/오류 custom = "custom" # 직접 입력 class DepartmentType(str, enum.Enum): production = "production" # 생산 quality = "quality" # 품질 purchasing = "purchasing" # 구매 design = "design" # 설계 sales = "sales" # 영업 class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) username = Column(String, unique=True, index=True, nullable=False) hashed_password = Column(String, nullable=False) full_name = Column(String) role = Column(Enum(UserRole), default=UserRole.user) department = Column(Enum(DepartmentType)) # 부서 정보 추가 is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=get_kst_now) # Relationships issues = relationship("Issue", back_populates="reporter", foreign_keys="Issue.reporter_id") reviewed_issues = relationship("Issue", foreign_keys="Issue.reviewed_by_id") daily_works = relationship("DailyWork", back_populates="created_by") projects = relationship("Project", back_populates="created_by") page_permissions = relationship("UserPagePermission", back_populates="user", foreign_keys="UserPagePermission.user_id") class UserPagePermission(Base): __tablename__ = "user_page_permissions" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) page_name = Column(String(50), nullable=False) can_access = Column(Boolean, default=False) granted_by_id = Column(Integer, ForeignKey("users.id")) granted_at = Column(DateTime, default=get_kst_now) notes = Column(Text) # Relationships user = relationship("User", back_populates="page_permissions", foreign_keys=[user_id]) granted_by = relationship("User", foreign_keys=[granted_by_id], post_update=True) # Unique constraint __table_args__ = ( Index('idx_user_page_permissions_user_id', 'user_id'), Index('idx_user_page_permissions_page_name', 'page_name'), ) class Issue(Base): __tablename__ = "issues" id = Column(Integer, primary_key=True, index=True) photo_path = Column(String) photo_path2 = Column(String) # 두 번째 사진 경로 category = Column(Enum(IssueCategory), nullable=False) description = Column(Text, nullable=False) status = Column(Enum(IssueStatus), default=IssueStatus.new) reporter_id = Column(Integer, ForeignKey("users.id")) project_id = Column(BigInteger, ForeignKey("projects.id")) report_date = Column(DateTime, default=get_kst_now) work_hours = Column(Float, default=0) detail_notes = Column(Text) # 수신함 워크플로우 관련 컬럼들 review_status = Column(Enum(ReviewStatus), default=ReviewStatus.pending_review) disposal_reason = Column(Enum(DisposalReasonType)) custom_disposal_reason = Column(Text) disposed_at = Column(DateTime) reviewed_by_id = Column(Integer, ForeignKey("users.id")) reviewed_at = Column(DateTime) original_data = Column(JSONB) # 원본 데이터 보존 modification_log = Column(JSONB, default=lambda: []) # 수정 이력 # 중복 신고 추적 시스템 duplicate_of_issue_id = Column(Integer, ForeignKey("issues.id")) # 중복 대상 이슈 ID duplicate_reporters = Column(JSONB, default=lambda: []) # 중복 신고자 목록 # 관리함에서 사용할 추가 필드들 completion_photo_path = Column(String) # 완료 사진 경로 solution = Column(Text) # 해결방안 (관리함에서 입력) responsible_department = Column(Enum(DepartmentType)) # 담당부서 responsible_person = Column(String(100)) # 담당자 expected_completion_date = Column(DateTime) # 조치 예상일 actual_completion_date = Column(DateTime) # 완료 확인일 cause_department = Column(Enum(DepartmentType)) # 원인부서 management_comment = Column(Text) # ISSUE에 대한 의견 project_sequence_no = Column(Integer) # 프로젝트별 순번 (No) final_description = Column(Text) # 최종 내용 (수정본 또는 원본) final_category = Column(Enum(IssueCategory)) # 최종 카테고리 (수정본 또는 원본) # 추가 정보 필드들 (관리함에서 기록용) responsible_person_detail = Column(String(200)) # 해당자 상세 정보 cause_detail = Column(Text) # 원인 상세 정보 additional_info_updated_at = Column(DateTime) # 추가 정보 입력 시간 additional_info_updated_by_id = Column(Integer, ForeignKey("users.id")) # 추가 정보 입력자 # 완료 신청 관련 필드들 completion_requested_at = Column(DateTime) # 완료 신청 시간 completion_requested_by_id = Column(Integer, ForeignKey("users.id")) # 완료 신청자 completion_photo_path = Column(String(500)) # 완료 사진 경로 completion_comment = Column(Text) # 완료 코멘트 # Relationships reporter = relationship("User", back_populates="issues", foreign_keys=[reporter_id]) reviewer = relationship("User", foreign_keys=[reviewed_by_id], overlaps="reviewed_issues") project = relationship("Project", back_populates="issues") duplicate_of = relationship("Issue", remote_side=[id], foreign_keys=[duplicate_of_issue_id]) class Project(Base): __tablename__ = "projects" id = Column(BigInteger, primary_key=True, index=True) job_no = Column(String, unique=True, nullable=False, index=True) project_name = Column(String, nullable=False) created_by_id = Column(Integer, ForeignKey("users.id")) created_at = Column(DateTime, default=get_kst_now) is_active = Column(Boolean, default=True) # Relationships created_by = relationship("User", back_populates="projects") issues = relationship("Issue", back_populates="project") class DailyWork(Base): __tablename__ = "daily_works" id = Column(Integer, primary_key=True, index=True) date = Column(DateTime, nullable=False, index=True) worker_count = Column(Integer, nullable=False) regular_hours = Column(Float, nullable=False) overtime_workers = Column(Integer, default=0) overtime_hours = Column(Float, default=0) overtime_total = Column(Float, default=0) total_hours = Column(Float, nullable=False) created_by_id = Column(Integer, ForeignKey("users.id")) created_at = Column(DateTime, default=get_kst_now) # Relationships created_by = relationship("User", back_populates="daily_works") class ProjectDailyWork(Base): __tablename__ = "project_daily_works" id = Column(Integer, primary_key=True, index=True) date = Column(DateTime, nullable=False, index=True) project_id = Column(BigInteger, ForeignKey("projects.id"), nullable=False) hours = Column(Float, nullable=False) created_by_id = Column(Integer, ForeignKey("users.id")) created_at = Column(DateTime, default=get_kst_now) # Relationships project = relationship("Project") created_by = relationship("User")