feat(tkeg): tkeg BOM 자재관리 서비스 초기 세팅 (api + web + docker-compose)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
471
tkeg/api/app/models.py
Normal file
471
tkeg/api/app/models.py
Normal file
@@ -0,0 +1,471 @@
|
||||
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)
|
||||
revision = Column(String(20), default='Rev.0')
|
||||
upload_date = Column(DateTime, default=datetime.utcnow)
|
||||
uploaded_by = Column(String(100))
|
||||
file_type = Column(String(10))
|
||||
file_size = 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)
|
||||
original_description = Column(Text, nullable=False)
|
||||
classified_category = Column(String(50))
|
||||
classified_subcategory = Column(String(100))
|
||||
material_grade = Column(String(50))
|
||||
schedule = Column(String(20))
|
||||
size_spec = Column(String(50))
|
||||
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))
|
||||
is_verified = Column(Boolean, default=False)
|
||||
verified_by = Column(String(50))
|
||||
verified_at = Column(DateTime)
|
||||
drawing_reference = Column(String(100))
|
||||
notes = Column(Text)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
# 추가 필드들
|
||||
main_nom = Column(String(50))
|
||||
red_nom = Column(String(50))
|
||||
purchase_confirmed = Column(Boolean, default=False)
|
||||
purchase_confirmed_at = Column(DateTime)
|
||||
purchase_status = Column(String(20), default='not_purchased')
|
||||
purchase_confirmed_by = Column(String(100))
|
||||
confirmed_quantity = Column(Numeric(10, 3))
|
||||
revision_status = Column(String(20), default='active')
|
||||
material_hash = Column(String(100))
|
||||
normalized_description = Column(Text)
|
||||
full_material_grade = Column(String(100))
|
||||
row_number = Column(Integer)
|
||||
length = Column(Numeric(10, 3))
|
||||
brand = Column(String(100))
|
||||
user_requirement = Column(Text)
|
||||
total_length = Column(Numeric(10, 3))
|
||||
|
||||
# 관계 설정
|
||||
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")
|
||||
Reference in New Issue
Block a user