from sqlalchemy import Column, Integer, String, Boolean, DateTime, Text, Numeric, ForeignKey, JSON from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship from datetime import datetime Base = declarative_base() class Project(Base): __tablename__ = "projects" id = Column(Integer, primary_key=True, index=True) official_project_code = Column(String(50), unique=True, index=True) project_name = Column(String(200), nullable=False) client_name = Column(String(100)) design_project_code = Column(String(50)) design_project_name = Column(String(200)) is_code_matched = Column(Boolean, default=False) matched_by = Column(String(100)) matched_at = Column(DateTime) status = Column(String(20), default='active') created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) description = Column(Text) notes = Column(Text) # 관계 설정 files = relationship("File", back_populates="project") class File(Base): __tablename__ = "files" id = Column(Integer, primary_key=True, index=True) project_id = Column(Integer, ForeignKey("projects.id")) filename = Column(String(255), nullable=False) original_filename = Column(String(255), nullable=False) file_path = Column(String(500), nullable=False) job_no = Column(String(50)) # 작업 번호 revision = Column(String(20), default='Rev.0') bom_name = Column(String(200)) # BOM 이름 description = Column(Text) # 파일 설명 upload_date = Column(DateTime, default=datetime.utcnow) uploaded_by = Column(String(100)) file_type = Column(String(10)) file_size = Column(Integer) parsed_count = Column(Integer) # 파싱된 자재 수 is_active = Column(Boolean, default=True) # 관계 설정 project = relationship("Project", back_populates="files") materials = relationship("Material", back_populates="file") class Material(Base): __tablename__ = "materials" id = Column(Integer, primary_key=True, index=True) file_id = Column(Integer, ForeignKey("files.id")) line_number = Column(Integer) row_number = Column(Integer) # 업로드 시 행 번호 original_description = Column(Text, nullable=False) classified_category = Column(String(50)) classified_subcategory = Column(String(100)) material_grade = Column(String(50)) full_material_grade = Column(Text) # 전체 재질명 (ASTM A312 TP304 등) schedule = Column(String(20)) size_spec = Column(String(50)) main_nom = Column(String(50)) # 주 사이즈 (4", 150A 등) red_nom = Column(String(50)) # 축소 사이즈 (Reducing 피팅/플랜지용) quantity = Column(Numeric(10, 3), nullable=False) unit = Column(String(10), nullable=False) length = Column(Numeric(10, 3)) # 길이 정보 drawing_name = Column(String(100)) area_code = Column(String(20)) line_no = Column(String(50)) classification_confidence = Column(Numeric(3, 2)) classification_details = Column(JSON) # 분류 상세 정보 (JSON) is_verified = Column(Boolean, default=False) verified_by = Column(String(50)) verified_at = Column(DateTime) # 구매 관련 필드 purchase_confirmed = Column(Boolean, default=False) confirmed_quantity = Column(Numeric(10, 3)) purchase_status = Column(String(20)) purchase_confirmed_by = Column(String(100)) purchase_confirmed_at = Column(DateTime) # 리비전 관리 필드 revision_status = Column(String(20)) # 'new', 'changed', 'inventory', 'deleted_not_purchased' material_hash = Column(String(64)) # 자재 비교용 해시 normalized_description = Column(Text) # 정규화된 설명 drawing_reference = Column(String(100)) notes = Column(Text) created_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 file = relationship("File", back_populates="materials") # ========== 자재 규격/재질 기준표 테이블들 ========== class MaterialStandard(Base): """자재 규격 표준 (ASTM, KS, JIS 등)""" __tablename__ = "material_standards" id = Column(Integer, primary_key=True, index=True) standard_code = Column(String(20), unique=True, nullable=False, index=True) # ASTM_ASME, KS, JIS standard_name = Column(String(100), nullable=False) # 미국재질학회, 한국산업표준, 일본공업규격 description = Column(Text) country = Column(String(50)) # USA, KOREA, JAPAN is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 categories = relationship("MaterialCategory", back_populates="standard") class MaterialCategory(Base): """제조방식별 카테고리 (FORGED, WELDED, CAST 등)""" __tablename__ = "material_categories" id = Column(Integer, primary_key=True, index=True) standard_id = Column(Integer, ForeignKey("material_standards.id")) category_code = Column(String(50), nullable=False) # FORGED_GRADES, WELDED_GRADES, CAST_GRADES category_name = Column(String(100), nullable=False) # 단조품, 용접품, 주조품 description = Column(Text) is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 standard = relationship("MaterialStandard", back_populates="categories") specifications = relationship("MaterialSpecification", back_populates="category") class MaterialSpecification(Base): """구체적인 규격 (A182, A105, D3507 등)""" __tablename__ = "material_specifications" id = Column(Integer, primary_key=True, index=True) category_id = Column(Integer, ForeignKey("material_categories.id")) spec_code = Column(String(20), nullable=False) # A182, A105, D3507 spec_name = Column(String(100), nullable=False) # 탄소강 단조품, 배관용 탄소강관 description = Column(Text) material_type = Column(String(50)) # carbon_alloy, stainless, carbon manufacturing = Column(String(50)) # FORGED, WELDED_FABRICATED, CAST, SEAMLESS pressure_rating = Column(String(100)) # 150LB ~ 9000LB is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 category = relationship("MaterialCategory", back_populates="specifications") grades = relationship("MaterialGrade", back_populates="specification") patterns = relationship("MaterialPattern", back_populates="specification") class MaterialGrade(Base): """등급별 상세 정보 (F1, F5, WPA, WPB 등)""" __tablename__ = "material_grades" id = Column(Integer, primary_key=True, index=True) specification_id = Column(Integer, ForeignKey("material_specifications.id")) grade_code = Column(String(20), nullable=False) # F1, F5, WPA, WPB grade_name = Column(String(100)) composition = Column(String(200)) # 0.5Mo, 5Cr-0.5Mo, 18Cr-8Ni applications = Column(String(200)) # 중온용, 고온용, 저압용 temp_max = Column(String(50)) # 482°C, 649°C temp_range = Column(String(100)) # -29°C ~ 400°C yield_strength = Column(String(50)) # 30 ksi, 35 ksi tensile_strength = Column(String(50)) corrosion_resistance = Column(String(50)) # 보통, 우수 stabilizer = Column(String(50)) # Titanium, Niobium base_grade = Column(String(20)) # 304, 316 is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 specification = relationship("MaterialSpecification", back_populates="grades") class MaterialPattern(Base): """정규식 패턴들""" __tablename__ = "material_patterns" id = Column(Integer, primary_key=True, index=True) specification_id = Column(Integer, ForeignKey("material_specifications.id")) pattern = Column(Text, nullable=False) # 정규식 패턴 description = Column(String(200)) priority = Column(Integer, default=1) # 패턴 우선순위 is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 specification = relationship("MaterialSpecification", back_populates="patterns") class SpecialMaterial(Base): """특수 재질 (INCONEL, HASTELLOY, TITANIUM 등)""" __tablename__ = "special_materials" id = Column(Integer, primary_key=True, index=True) material_type = Column(String(50), nullable=False) # SUPER_ALLOYS, TITANIUM, COPPER_ALLOYS material_name = Column(String(100), nullable=False) # INCONEL, HASTELLOY, TITANIUM description = Column(Text) composition = Column(String(200)) applications = Column(Text) temp_max = Column(String(50)) manufacturing = Column(String(50)) is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 grades = relationship("SpecialMaterialGrade", back_populates="material") patterns = relationship("SpecialMaterialPattern", back_populates="material") class SpecialMaterialGrade(Base): """특수 재질 등급""" __tablename__ = "special_material_grades" id = Column(Integer, primary_key=True, index=True) material_id = Column(Integer, ForeignKey("special_materials.id")) grade_code = Column(String(20), nullable=False) # 600, 625, C276 composition = Column(String(200)) applications = Column(String(200)) temp_max = Column(String(50)) strength = Column(String(50)) purity = Column(String(100)) corrosion = Column(String(50)) is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 material = relationship("SpecialMaterial", back_populates="grades") class SpecialMaterialPattern(Base): """특수 재질 정규식 패턴""" __tablename__ = "special_material_patterns" id = Column(Integer, primary_key=True, index=True) material_id = Column(Integer, ForeignKey("special_materials.id")) pattern = Column(Text, nullable=False) description = Column(String(200)) priority = Column(Integer, default=1) is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 material = relationship("SpecialMaterial", back_populates="patterns") # ========== 파이프 상세 정보 및 사용자 요구사항 테이블 ========== class PipeDetail(Base): """파이프 상세 정보""" __tablename__ = "pipe_details" id = Column(Integer, primary_key=True, index=True) file_id = Column(Integer, ForeignKey("files.id"), nullable=False) # 재질 정보 material_standard = Column(String(50)) # ASTM, KS, JIS 등 material_grade = Column(String(50)) # A106, A53, STPG370 등 material_type = Column(String(50)) # CARBON, STAINLESS 등 # 파이프 특화 정보 manufacturing_method = Column(String(50)) # SEAMLESS, WELDED, CAST end_preparation = Column(String(50)) # BOTH_ENDS_BEVELED, ONE_END_BEVELED, NO_BEVEL schedule = Column(String(50)) # SCH 10, 20, 40, 80 등 wall_thickness = Column(String(50)) # 벽두께 정보 # 치수 정보 nominal_size = Column(String(50)) # MAIN_NOM (인치, 직경) length_mm = Column(Numeric(10, 3)) # LENGTH (길이) # 신뢰도 material_confidence = Column(Numeric(3, 2)) manufacturing_confidence = Column(Numeric(3, 2)) end_prep_confidence = Column(Numeric(3, 2)) schedule_confidence = Column(Numeric(3, 2)) # 메타데이터 created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 file = relationship("File", backref="pipe_details") class RequirementType(Base): """요구사항 타입 마스터""" __tablename__ = "requirement_types" id = Column(Integer, primary_key=True, index=True) type_code = Column(String(50), unique=True, nullable=False) # 'IMPACT_TEST', 'HEAT_TREATMENT' 등 type_name = Column(String(100), nullable=False) # '임팩테스트', '열처리' 등 category = Column(String(50), nullable=False) # 'TEST', 'TREATMENT', 'CERTIFICATION', 'CUSTOM' 등 description = Column(Text) # 타입 설명 is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) # 관계 설정은 문자열 기반이므로 제거 class UserRequirement(Base): """사용자 추가 요구사항""" __tablename__ = "user_requirements" id = Column(Integer, primary_key=True, index=True) file_id = Column(Integer, ForeignKey("files.id"), nullable=False) material_id = Column(Integer, ForeignKey("materials.id"), nullable=True) # 자재 ID (개별 자재별 요구사항 연결) # 요구사항 타입 requirement_type = Column(String(50), nullable=False) # 'IMPACT_TEST', 'HEAT_TREATMENT', 'CUSTOM_SPEC', 'CERTIFICATION' 등 # 요구사항 내용 requirement_title = Column(String(200), nullable=False) # '임팩테스트', '열처리', '인증서' 등 requirement_description = Column(Text) # 상세 설명 requirement_spec = Column(Text) # 구체적 스펙 (예: "Charpy V-notch -20°C") # 상태 관리 status = Column(String(20), default='PENDING') # 'PENDING', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED' priority = Column(String(20), default='NORMAL') # 'LOW', 'NORMAL', 'HIGH', 'URGENT' # 담당자 정보 assigned_to = Column(String(100)) # 담당자명 due_date = Column(DateTime) # 완료 예정일 # 메타데이터 created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 file = relationship("File", backref="user_requirements") # ========== Tubing 시스템 모델들 ========== class TubingCategory(Base): """Tubing 카테고리 (일반, VCR, 위생용 등)""" __tablename__ = "tubing_categories" id = Column(Integer, primary_key=True, index=True) category_code = Column(String(20), unique=True, nullable=False) category_name = Column(String(100), nullable=False) description = Column(Text) is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 specifications = relationship("TubingSpecification", back_populates="category") class TubingSpecification(Base): """Tubing 규격 마스터""" __tablename__ = "tubing_specifications" id = Column(Integer, primary_key=True, index=True) category_id = Column(Integer, ForeignKey("tubing_categories.id")) spec_code = Column(String(50), unique=True, nullable=False) spec_name = Column(String(200), nullable=False) # 물리적 규격 outer_diameter_mm = Column(Numeric(8, 3)) wall_thickness_mm = Column(Numeric(6, 3)) inner_diameter_mm = Column(Numeric(8, 3)) # 재질 정보 material_grade = Column(String(100)) material_standard = Column(String(100)) # 압력/온도 등급 max_pressure_bar = Column(Numeric(8, 2)) max_temperature_c = Column(Numeric(6, 2)) min_temperature_c = Column(Numeric(6, 2)) # 표준 규격 standard_length_m = Column(Numeric(8, 3)) bend_radius_min_mm = Column(Numeric(8, 2)) # 기타 정보 surface_finish = Column(String(100)) hardness = Column(String(50)) notes = Column(Text) is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 category = relationship("TubingCategory", back_populates="specifications") products = relationship("TubingProduct", back_populates="specification") class TubingManufacturer(Base): """Tubing 제조사""" __tablename__ = "tubing_manufacturers" id = Column(Integer, primary_key=True, index=True) manufacturer_code = Column(String(20), unique=True, nullable=False) manufacturer_name = Column(String(200), nullable=False) country = Column(String(100)) website = Column(String(500)) contact_info = Column(JSON) # JSONB 타입 quality_certs = Column(JSON) # JSONB 타입 is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 products = relationship("TubingProduct", back_populates="manufacturer") class TubingProduct(Base): """제조사별 Tubing 제품 (품목번호 매핑)""" __tablename__ = "tubing_products" id = Column(Integer, primary_key=True, index=True) specification_id = Column(Integer, ForeignKey("tubing_specifications.id")) manufacturer_id = Column(Integer, ForeignKey("tubing_manufacturers.id")) # 제조사 품목번호 정보 manufacturer_part_number = Column(String(200), nullable=False) manufacturer_product_name = Column(String(300)) # 가격/공급 정보 list_price = Column(Numeric(12, 2)) currency = Column(String(10), default='KRW') lead_time_days = Column(Integer) minimum_order_qty = Column(Numeric(10, 3)) standard_packaging_qty = Column(Numeric(10, 3)) # 가용성 정보 availability_status = Column(String(50)) last_price_update = Column(DateTime) # 추가 정보 datasheet_url = Column(String(500)) catalog_page = Column(String(100)) notes = Column(Text) is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 specification = relationship("TubingSpecification", back_populates="products") manufacturer = relationship("TubingManufacturer", back_populates="products") material_mappings = relationship("MaterialTubingMapping", back_populates="tubing_product") class MaterialTubingMapping(Base): """BOM 자재와 Tubing 제품 매핑""" __tablename__ = "material_tubing_mapping" id = Column(Integer, primary_key=True, index=True) material_id = Column(Integer, ForeignKey("materials.id", ondelete="CASCADE")) tubing_product_id = Column(Integer, ForeignKey("tubing_products.id")) # 매핑 정보 confidence_score = Column(Numeric(3, 2)) mapping_method = Column(String(50)) mapped_by = Column(String(100)) mapped_at = Column(DateTime, default=datetime.utcnow) # 수량 정보 required_length_m = Column(Numeric(10, 3)) calculated_quantity = Column(Numeric(10, 3)) # 검증 정보 is_verified = Column(Boolean, default=False) verified_by = Column(String(100)) verified_at = Column(DateTime) notes = Column(Text) created_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 material = relationship("Material", backref="tubing_mappings") tubing_product = relationship("TubingProduct", back_populates="material_mappings") class SupportDetails(Base): __tablename__ = "support_details" id = Column(Integer, primary_key=True, index=True) material_id = Column(Integer, ForeignKey("materials.id"), nullable=False) file_id = Column(Integer, ForeignKey("files.id"), nullable=False) # 서포트 정보 support_type = Column(String(50)) support_subtype = Column(String(100)) load_rating = Column(String(50)) load_capacity = Column(String(50)) material_standard = Column(String(50)) material_grade = Column(String(50)) pipe_size = Column(String(50)) # 치수 정보 length_mm = Column(Numeric(10, 2)) width_mm = Column(Numeric(10, 2)) height_mm = Column(Numeric(10, 2)) # 분류 신뢰도 classification_confidence = Column(Numeric(3, 2)) # 시간 정보 created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # 관계 설정 material = relationship("Material") file = relationship("File") class PurchaseRequestItems(Base): __tablename__ = "purchase_request_items" id = Column(Integer, primary_key=True, index=True) request_id = Column(String(50), nullable=False) # 구매신청 ID material_id = Column(Integer, ForeignKey("materials.id"), nullable=False) # 수량 정보 quantity = Column(Integer, nullable=False) unit = Column(String(10), nullable=False) user_requirement = Column(Text) # 시간 정보 created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # 관계 설정 material = relationship("Material") class FittingDetails(Base): __tablename__ = "fitting_details" id = Column(Integer, primary_key=True, index=True) material_id = Column(Integer, ForeignKey("materials.id"), nullable=False) file_id = Column(Integer, ForeignKey("files.id"), nullable=False) # 피팅 정보 fitting_type = Column(String(50)) fitting_subtype = Column(String(100)) connection_type = Column(String(50)) material_standard = Column(String(50)) material_grade = Column(String(50)) nominal_size = Column(String(50)) wall_thickness = Column(String(50)) # 치수 정보 length_mm = Column(Numeric(10, 2)) width_mm = Column(Numeric(10, 2)) height_mm = Column(Numeric(10, 2)) # 분류 신뢰도 classification_confidence = Column(Numeric(3, 2)) # 시간 정보 created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # 관계 설정 material = relationship("Material") file = relationship("File") class FlangeDetails(Base): __tablename__ = "flange_details" id = Column(Integer, primary_key=True, index=True) material_id = Column(Integer, ForeignKey("materials.id"), nullable=False) file_id = Column(Integer, ForeignKey("files.id"), nullable=False) # 플랜지 정보 flange_type = Column(String(50)) flange_subtype = Column(String(100)) pressure_rating = Column(String(50)) material_standard = Column(String(50)) material_grade = Column(String(50)) nominal_size = Column(String(50)) # 치수 정보 outer_diameter = Column(Numeric(10, 2)) thickness = Column(Numeric(10, 2)) # 분류 신뢰도 classification_confidence = Column(Numeric(3, 2)) # 시간 정보 created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # 관계 설정 material = relationship("Material") file = relationship("File") class ValveDetails(Base): __tablename__ = "valve_details" id = Column(Integer, primary_key=True, index=True) material_id = Column(Integer, ForeignKey("materials.id"), nullable=False) file_id = Column(Integer, ForeignKey("files.id"), nullable=False) # 밸브 정보 valve_type = Column(String(50)) valve_subtype = Column(String(100)) connection_type = Column(String(50)) actuation_type = Column(String(50)) material_standard = Column(String(50)) material_grade = Column(String(50)) nominal_size = Column(String(50)) pressure_rating = Column(String(50)) # 분류 신뢰도 classification_confidence = Column(Numeric(3, 2)) # 시간 정보 created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # 관계 설정 material = relationship("Material") file = relationship("File") class GasketDetails(Base): __tablename__ = "gasket_details" id = Column(Integer, primary_key=True, index=True) material_id = Column(Integer, ForeignKey("materials.id"), nullable=False) file_id = Column(Integer, ForeignKey("files.id"), nullable=False) # 가스켓 정보 gasket_type = Column(String(50)) material_standard = Column(String(50)) material_grade = Column(String(50)) nominal_size = Column(String(50)) pressure_rating = Column(String(50)) filler_material = Column(String(50)) thickness = Column(Numeric(10, 2)) # 분류 신뢰도 classification_confidence = Column(Numeric(3, 2)) # 시간 정보 created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # 관계 설정 material = relationship("Material") file = relationship("File") class BoltDetails(Base): __tablename__ = "bolt_details" id = Column(Integer, primary_key=True, index=True) material_id = Column(Integer, ForeignKey("materials.id"), nullable=False) file_id = Column(Integer, ForeignKey("files.id"), nullable=False) # 볼트 정보 bolt_type = Column(String(50)) material_standard = Column(String(50)) material_grade = Column(String(50)) thread_size = Column(String(50)) length = Column(Numeric(10, 2)) pressure_rating = Column(String(50)) # 분류 신뢰도 classification_confidence = Column(Numeric(3, 2)) # 시간 정보 created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # 관계 설정 material = relationship("Material") file = relationship("File") class InstrumentDetails(Base): __tablename__ = "instrument_details" id = Column(Integer, primary_key=True, index=True) material_id = Column(Integer, ForeignKey("materials.id"), nullable=False) file_id = Column(Integer, ForeignKey("files.id"), nullable=False) # 계기 정보 instrument_type = Column(String(50)) instrument_subtype = Column(String(100)) connection_type = Column(String(50)) material_standard = Column(String(50)) material_grade = Column(String(50)) nominal_size = Column(String(50)) # 분류 신뢰도 classification_confidence = Column(Numeric(3, 2)) # 시간 정보 created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # 관계 설정 material = relationship("Material") file = relationship("File") class PurchaseRequests(Base): __tablename__ = "purchase_requests" request_id = Column(String(50), primary_key=True, index=True) request_no = Column(String(100), nullable=False) file_id = Column(Integer, ForeignKey("files.id"), nullable=False) job_no = Column(String(50), nullable=False) category = Column(String(50)) material_count = Column(Integer) excel_file_path = Column(String(500)) requested_by = Column(Integer) # ForeignKey 제거 requested_by_username = Column(String(100)) # 사용자명 직접 저장 # 시간 정보 created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # 관계 설정 file = relationship("File") # requested_by_user relationship 제거 class Jobs(Base): __tablename__ = "jobs" id = Column(Integer, primary_key=True, index=True) job_no = Column(String(50), unique=True, nullable=False) job_name = Column(String(200)) status = Column(String(20), default='active') created_at = Column(DateTime, default=datetime.utcnow) class PipeEndPreparations(Base): __tablename__ = "pipe_end_preparations" id = Column(Integer, primary_key=True, index=True) material_id = Column(Integer, ForeignKey("materials.id"), nullable=False) file_id = Column(Integer, ForeignKey("files.id"), nullable=False) end_prep_type = Column(String(50)) end_prep_standard = Column(String(50)) classification_confidence = Column(Numeric(3, 2)) created_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 material = relationship("Material") file = relationship("File") class MaterialPurchaseTracking(Base): __tablename__ = "material_purchase_tracking" id = Column(Integer, primary_key=True, index=True) material_id = Column(Integer, ForeignKey("materials.id"), nullable=False) file_id = Column(Integer, ForeignKey("files.id"), nullable=False) purchase_status = Column(String(20)) requested_quantity = Column(Integer) confirmed_quantity = Column(Integer) purchase_date = Column(DateTime) created_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 material = relationship("Material") file = relationship("File") class ExcelExports(Base): __tablename__ = "excel_exports" id = Column(Integer, primary_key=True, index=True) file_id = Column(Integer, ForeignKey("files.id"), nullable=False) export_type = Column(String(50)) file_path = Column(String(500)) exported_by = Column(Integer, ForeignKey("users.user_id")) created_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 file = relationship("File") # exported_by_user relationship 제거 class UserActivityLogs(Base): __tablename__ = "user_activity_logs" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.user_id")) activity_type = Column(String(50)) activity_description = Column(Text) created_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 # user relationship 제거 class ExcelExportHistory(Base): __tablename__ = "excel_export_history" export_id = Column(String(50), primary_key=True, index=True) file_id = Column(Integer, ForeignKey("files.id")) job_no = Column(String(50)) exported_by = Column(Integer) # ForeignKey 제거 exported_by_username = Column(String(100)) # 사용자명 직접 저장 export_date = Column(DateTime, default=datetime.utcnow) # 관계 설정 file = relationship("File") # exported_by_user relationship 제거 class ExportedMaterials(Base): __tablename__ = "exported_materials" id = Column(Integer, primary_key=True, index=True) export_id = Column(String(50), ForeignKey("excel_export_history.export_id")) material_id = Column(Integer, ForeignKey("materials.id")) quantity = Column(Integer) status = Column(String(20)) # 관계 설정 export_history = relationship("ExcelExportHistory") material = relationship("Material") class PurchaseStatusHistory(Base): __tablename__ = "purchase_status_history" id = Column(Integer, primary_key=True, index=True) material_id = Column(Integer, ForeignKey("materials.id")) old_status = Column(String(20)) new_status = Column(String(20)) changed_by = Column(Integer, ForeignKey("users.user_id")) changed_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 material = relationship("Material") # changed_by_user relationship 제거 # ========== 간단한 리비전 관리 시스템 ========== class SimpleRevisionComparison(Base): """간단한 리비전 비교 결과 저장""" __tablename__ = "simple_revision_comparisons" id = Column(Integer, primary_key=True, index=True) job_no = Column(String(50), nullable=False, index=True) current_file_id = Column(Integer, ForeignKey("files.id"), nullable=False) previous_file_id = Column(Integer, ForeignKey("files.id"), nullable=False) category = Column(String(50), nullable=False) # 비교 대상 카테고리 # 비교 결과 통계 added_count = Column(Integer, default=0) removed_count = Column(Integer, default=0) changed_count = Column(Integer, default=0) unchanged_count = Column(Integer, default=0) # 구매 상태별 통계 purchased_affected = Column(Integer, default=0) unpurchased_affected = Column(Integer, default=0) inventory_count = Column(Integer, default=0) # 메타데이터 comparison_data = Column(JSON) # 상세 비교 결과 created_at = Column(DateTime, default=datetime.utcnow) created_by_username = Column(String(100)) # 관계 설정 (간단하게) current_file = relationship("File", foreign_keys=[current_file_id]) previous_file = relationship("File", foreign_keys=[previous_file_id]) class SimpleRevisionMaterial(Base): """간단한 리비전 자재 변경 로그""" __tablename__ = "simple_revision_materials" id = Column(Integer, primary_key=True, index=True) comparison_id = Column(Integer, ForeignKey("simple_revision_comparisons.id"), nullable=False) material_id = Column(Integer, ForeignKey("materials.id")) change_type = Column(String(20), nullable=False) # 'added', 'removed', 'changed', 'quantity_changed' revision_action = Column(String(30)) # 'maintain', 'additional_purchase', 'inventory', 'delete', 'quantity_update' # 수량 변경 정보 quantity_before = Column(Numeric(10, 3)) quantity_after = Column(Numeric(10, 3)) quantity_difference = Column(Numeric(10, 3)) purchase_status = Column(String(20)) # 'purchased', 'not_purchased' created_at = Column(DateTime, default=datetime.utcnow) # 관계 설정 (간단하게) comparison = relationship("SimpleRevisionComparison") material = relationship("Material") # ========== 간단한 리비전 시스템 완료 ========== # 나머지 복잡한 테이블들은 제거하고 필요시 추가 # 기존 복잡한 모델들 제거됨 (PipeLengthCalculation, MaterialPurchaseHistory 등) # 필요시 간단한 구조로 다시 추가 예정