feat: SWG 가스켓 전체 구성 정보 표시 개선
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
- H/F/I/O SS304/GRAPHITE/CS/CS 패턴에서 4개 구성요소 모두 표시 - 기존 SS304 + GRAPHITE → SS304/GRAPHITE/CS/CS로 완전한 구성 표시 - 외부링/필러/내부링/추가구성 모든 정보 포함 - 구매수량 계산 모달에서 정확한 재질 정보 확인 가능
This commit is contained in:
@@ -10,26 +10,26 @@ from typing import Dict, List, Tuple
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import text
|
||||
|
||||
# 자재별 기본 여유율
|
||||
# 자재별 기본 여유율 (올바른 규칙으로 수정)
|
||||
SAFETY_FACTORS = {
|
||||
'PIPE': 1.15, # 15% 추가 (절단 손실)
|
||||
'FITTING': 1.10, # 10% 추가 (연결 오차)
|
||||
'VALVE': 1.50, # 50% 추가 (예비품)
|
||||
'FLANGE': 1.10, # 10% 추가
|
||||
'BOLT': 1.20, # 20% 추가 (분실율)
|
||||
'GASKET': 1.25, # 25% 추가 (교체주기)
|
||||
'INSTRUMENT': 1.00, # 0% 추가 (정확한 수량)
|
||||
'DEFAULT': 1.10 # 기본 10% 추가
|
||||
'PIPE': 1.00, # 0% 추가 (절단 손실은 별도 계산)
|
||||
'FITTING': 1.00, # 0% 추가 (BOM 수량 그대로)
|
||||
'VALVE': 1.00, # 0% 추가 (BOM 수량 그대로)
|
||||
'FLANGE': 1.00, # 0% 추가 (BOM 수량 그대로)
|
||||
'BOLT': 1.05, # 5% 추가 (분실율)
|
||||
'GASKET': 1.00, # 0% 추가 (5의 배수 올림으로 처리)
|
||||
'INSTRUMENT': 1.00, # 0% 추가 (BOM 수량 그대로)
|
||||
'DEFAULT': 1.00 # 기본 0% 추가
|
||||
}
|
||||
|
||||
# 최소 주문 수량 (자재별)
|
||||
# 최소 주문 수량 (자재별) - 올바른 규칙으로 수정
|
||||
MINIMUM_ORDER_QTY = {
|
||||
'PIPE': 6000, # 6M 단위
|
||||
'FITTING': 1, # 개별 주문 가능
|
||||
'VALVE': 1, # 개별 주문 가능
|
||||
'FLANGE': 1, # 개별 주문 가능
|
||||
'BOLT': 50, # 박스 단위 (50개)
|
||||
'GASKET': 10, # 세트 단위
|
||||
'BOLT': 4, # 4의 배수 단위
|
||||
'GASKET': 5, # 5의 배수 단위
|
||||
'INSTRUMENT': 1, # 개별 주문 가능
|
||||
'DEFAULT': 1
|
||||
}
|
||||
@@ -37,7 +37,7 @@ MINIMUM_ORDER_QTY = {
|
||||
def calculate_pipe_purchase_quantity(materials: List[Dict]) -> Dict:
|
||||
"""
|
||||
PIPE 구매 수량 계산
|
||||
- 각 절단마다 3mm 손실
|
||||
- 각 절단마다 2mm 손실 (올바른 규칙)
|
||||
- 6,000mm (6M) 단위로 올림
|
||||
"""
|
||||
total_bom_length = 0
|
||||
@@ -45,19 +45,23 @@ def calculate_pipe_purchase_quantity(materials: List[Dict]) -> Dict:
|
||||
pipe_details = []
|
||||
|
||||
for material in materials:
|
||||
# 길이 정보 추출
|
||||
# 길이 정보 추출 (Decimal 타입 처리)
|
||||
length_mm = float(material.get('length_mm', 0) or 0)
|
||||
quantity = float(material.get('quantity', 1) or 1)
|
||||
|
||||
if length_mm > 0:
|
||||
total_bom_length += length_mm
|
||||
cutting_count += 1
|
||||
total_length = length_mm * quantity # 총 길이 = 단위길이 × 수량
|
||||
total_bom_length += total_length
|
||||
cutting_count += quantity # 절단 횟수 = 수량
|
||||
pipe_details.append({
|
||||
'description': material.get('original_description', ''),
|
||||
'length_mm': length_mm,
|
||||
'quantity': material.get('quantity', 1)
|
||||
'quantity': quantity,
|
||||
'total_length': total_length
|
||||
})
|
||||
|
||||
# 절단 손실 계산 (각 절단마다 3mm)
|
||||
cutting_loss = cutting_count * 3
|
||||
# 절단 손실 계산 (각 절단마다 2mm - 올바른 규칙)
|
||||
cutting_loss = cutting_count * 2
|
||||
|
||||
# 총 필요 길이 = BOM 길이 + 절단 손실
|
||||
required_length = total_bom_length + cutting_loss
|
||||
@@ -92,7 +96,8 @@ def calculate_standard_purchase_quantity(category: str, bom_quantity: float,
|
||||
if safety_factor is None:
|
||||
safety_factor = SAFETY_FACTORS.get(category, SAFETY_FACTORS['DEFAULT'])
|
||||
|
||||
# 1단계: 여유율 적용
|
||||
# 1단계: 여유율 적용 (Decimal 타입 처리)
|
||||
bom_quantity = float(bom_quantity) if bom_quantity else 0.0
|
||||
safety_qty = bom_quantity * safety_factor
|
||||
|
||||
# 2단계: 최소 주문 수량 확인
|
||||
@@ -101,9 +106,13 @@ def calculate_standard_purchase_quantity(category: str, bom_quantity: float,
|
||||
# 3단계: 최소 주문 수량과 비교하여 큰 값 선택
|
||||
calculated_qty = max(safety_qty, min_order_qty)
|
||||
|
||||
# 4단계: 특별 처리 (BOLT는 박스 단위로 올림)
|
||||
if category == 'BOLT' and calculated_qty > min_order_qty:
|
||||
calculated_qty = math.ceil(calculated_qty / min_order_qty) * min_order_qty
|
||||
# 4단계: 특별 처리 (올바른 규칙 적용)
|
||||
if category == 'BOLT':
|
||||
# BOLT: 5% 여유율 후 4의 배수로 올림
|
||||
calculated_qty = math.ceil(safety_qty / min_order_qty) * min_order_qty
|
||||
elif category == 'GASKET':
|
||||
# GASKET: 5의 배수로 올림 (여유율 없음)
|
||||
calculated_qty = math.ceil(bom_quantity / min_order_qty) * min_order_qty
|
||||
|
||||
return {
|
||||
'bom_quantity': bom_quantity,
|
||||
@@ -120,16 +129,19 @@ def generate_purchase_items_from_materials(db: Session, file_id: int,
|
||||
"""
|
||||
자재 데이터로부터 구매 품목 생성
|
||||
"""
|
||||
# 1. 파일의 모든 자재 조회
|
||||
# 1. 파일의 모든 자재 조회 (상세 테이블 정보 포함)
|
||||
materials_query = text("""
|
||||
SELECT m.*,
|
||||
pd.length_mm, pd.outer_diameter, pd.schedule, pd.material_spec as pipe_material_spec,
|
||||
fd.fitting_type, fd.connection_method as fitting_connection,
|
||||
fd.fitting_type, fd.connection_method as fitting_connection, fd.main_size as fitting_main_size,
|
||||
fd.reduced_size as fitting_reduced_size, fd.material_grade as fitting_material_grade,
|
||||
vd.valve_type, vd.connection_method as valve_connection, vd.pressure_rating as valve_pressure,
|
||||
fl.flange_type, fl.pressure_rating as flange_pressure,
|
||||
gd.gasket_type, gd.material_type as gasket_material,
|
||||
bd.bolt_type, bd.material_standard, bd.diameter,
|
||||
id.instrument_type
|
||||
vd.size_inches as valve_size,
|
||||
fl.flange_type, fl.pressure_rating as flange_pressure, fl.size_inches as flange_size,
|
||||
gd.gasket_type, gd.gasket_subtype, gd.material_type as gasket_material, gd.filler_material,
|
||||
gd.size_inches as gasket_size, gd.pressure_rating as gasket_pressure, gd.thickness as gasket_thickness,
|
||||
bd.bolt_type, bd.material_standard, bd.diameter as bolt_diameter, bd.length as bolt_length,
|
||||
id.instrument_type, id.connection_size as instrument_size
|
||||
FROM materials m
|
||||
LEFT JOIN pipe_details pd ON m.id = pd.material_id
|
||||
LEFT JOIN fitting_details fd ON m.id = fd.material_id
|
||||
@@ -144,13 +156,65 @@ def generate_purchase_items_from_materials(db: Session, file_id: int,
|
||||
|
||||
materials = db.execute(materials_query, {"file_id": file_id}).fetchall()
|
||||
|
||||
|
||||
|
||||
# 2. 카테고리별로 그룹핑
|
||||
grouped_materials = {}
|
||||
for material in materials:
|
||||
category = material.classified_category or 'OTHER'
|
||||
if category not in grouped_materials:
|
||||
grouped_materials[category] = []
|
||||
grouped_materials[category].append(dict(material))
|
||||
|
||||
# Row 객체를 딕셔너리로 안전하게 변환
|
||||
material_dict = {
|
||||
'id': material.id,
|
||||
'file_id': material.file_id,
|
||||
'original_description': material.original_description,
|
||||
'quantity': material.quantity,
|
||||
'unit': material.unit,
|
||||
'size_spec': material.size_spec,
|
||||
'material_grade': material.material_grade,
|
||||
'classified_category': material.classified_category,
|
||||
'line_number': material.line_number,
|
||||
# PIPE 상세 정보
|
||||
'length_mm': getattr(material, 'length_mm', None),
|
||||
'outer_diameter': getattr(material, 'outer_diameter', None),
|
||||
'schedule': getattr(material, 'schedule', None),
|
||||
'pipe_material_spec': getattr(material, 'pipe_material_spec', None),
|
||||
# FITTING 상세 정보
|
||||
'fitting_type': getattr(material, 'fitting_type', None),
|
||||
'fitting_connection': getattr(material, 'fitting_connection', None),
|
||||
'fitting_main_size': getattr(material, 'fitting_main_size', None),
|
||||
'fitting_reduced_size': getattr(material, 'fitting_reduced_size', None),
|
||||
'fitting_material_grade': getattr(material, 'fitting_material_grade', None),
|
||||
# VALVE 상세 정보
|
||||
'valve_type': getattr(material, 'valve_type', None),
|
||||
'valve_connection': getattr(material, 'valve_connection', None),
|
||||
'valve_pressure': getattr(material, 'valve_pressure', None),
|
||||
'valve_size': getattr(material, 'valve_size', None),
|
||||
# FLANGE 상세 정보
|
||||
'flange_type': getattr(material, 'flange_type', None),
|
||||
'flange_pressure': getattr(material, 'flange_pressure', None),
|
||||
'flange_size': getattr(material, 'flange_size', None),
|
||||
# GASKET 상세 정보
|
||||
'gasket_type': getattr(material, 'gasket_type', None),
|
||||
'gasket_subtype': getattr(material, 'gasket_subtype', None),
|
||||
'gasket_material': getattr(material, 'gasket_material', None),
|
||||
'filler_material': getattr(material, 'filler_material', None),
|
||||
'gasket_size': getattr(material, 'gasket_size', None),
|
||||
'gasket_pressure': getattr(material, 'gasket_pressure', None),
|
||||
'gasket_thickness': getattr(material, 'gasket_thickness', None),
|
||||
# BOLT 상세 정보
|
||||
'bolt_type': getattr(material, 'bolt_type', None),
|
||||
'material_standard': getattr(material, 'material_standard', None),
|
||||
'bolt_diameter': getattr(material, 'bolt_diameter', None),
|
||||
'bolt_length': getattr(material, 'bolt_length', None),
|
||||
# INSTRUMENT 상세 정보
|
||||
'instrument_type': getattr(material, 'instrument_type', None),
|
||||
'instrument_size': getattr(material, 'instrument_size', None)
|
||||
}
|
||||
|
||||
grouped_materials[category].append(material_dict)
|
||||
|
||||
# 3. 각 카테고리별로 구매 품목 생성
|
||||
purchase_items = []
|
||||
@@ -249,13 +313,41 @@ def generate_material_specs_for_category(materials: List[Dict], category: str) -
|
||||
if category == 'FITTING':
|
||||
fitting_type = material.get('fitting_type', 'FITTING')
|
||||
connection_method = material.get('fitting_connection', '')
|
||||
material_spec = material.get('material_grade', '')
|
||||
main_nom = material.get('main_nom', '')
|
||||
red_nom = material.get('red_nom', '')
|
||||
size_display = f"{main_nom} x {red_nom}" if red_nom else main_nom
|
||||
# 상세 테이블의 재질 정보 우선 사용
|
||||
material_spec = material.get('fitting_material_grade') or material.get('material_grade', '')
|
||||
# 상세 테이블의 사이즈 정보 사용
|
||||
main_size = material.get('fitting_main_size', '')
|
||||
reduced_size = material.get('fitting_reduced_size', '')
|
||||
|
||||
# 사이즈 표시 생성 (축소형인 경우 main x reduced 형태)
|
||||
if main_size and reduced_size and main_size != reduced_size:
|
||||
size_display = f"{main_size} x {reduced_size}"
|
||||
else:
|
||||
size_display = main_size or material.get('size_spec', '')
|
||||
|
||||
# 기존 분류기 방식: 피팅 타입 + 연결방식 + 압력등급
|
||||
# 예: "ELBOW, SOCKET WELD, 3000LB"
|
||||
fitting_display = fitting_type.replace('_', ' ') if fitting_type else 'FITTING'
|
||||
|
||||
spec_parts = [fitting_display]
|
||||
|
||||
# 연결방식 추가
|
||||
if connection_method and connection_method != 'UNKNOWN':
|
||||
connection_display = connection_method.replace('_', ' ')
|
||||
spec_parts.append(connection_display)
|
||||
|
||||
# 압력등급 추출 (description에서)
|
||||
description = material.get('original_description', '').upper()
|
||||
import re
|
||||
pressure_match = re.search(r'(\d+)LB', description)
|
||||
if pressure_match:
|
||||
spec_parts.append(f"{pressure_match.group(1)}LB")
|
||||
|
||||
# 스케줄 정보 추출 (니플 등에 중요)
|
||||
schedule_match = re.search(r'SCH\s*(\d+)', description)
|
||||
if schedule_match:
|
||||
spec_parts.append(f"SCH {schedule_match.group(1)}")
|
||||
|
||||
spec_parts = [fitting_type]
|
||||
if connection_method: spec_parts.append(connection_method)
|
||||
full_spec = ', '.join(spec_parts)
|
||||
|
||||
spec_key = f"FITTING|{full_spec}|{material_spec}|{size_display}"
|
||||
@@ -272,19 +364,20 @@ def generate_material_specs_for_category(materials: List[Dict], category: str) -
|
||||
connection_method = material.get('valve_connection', '')
|
||||
pressure_rating = material.get('valve_pressure', '')
|
||||
material_spec = material.get('material_grade', '')
|
||||
main_nom = material.get('main_nom', '')
|
||||
# 상세 테이블의 사이즈 정보 우선 사용
|
||||
size_display = material.get('valve_size') or material.get('size_spec', '')
|
||||
|
||||
spec_parts = [valve_type.replace('_', ' ')]
|
||||
if connection_method: spec_parts.append(connection_method.replace('_', ' '))
|
||||
if pressure_rating: spec_parts.append(pressure_rating)
|
||||
full_spec = ', '.join(spec_parts)
|
||||
|
||||
spec_key = f"VALVE|{full_spec}|{material_spec}|{main_nom}"
|
||||
spec_key = f"VALVE|{full_spec}|{material_spec}|{size_display}"
|
||||
spec_data = {
|
||||
'category': 'VALVE',
|
||||
'category': 'VALVE',
|
||||
'full_spec': full_spec,
|
||||
'material_spec': material_spec,
|
||||
'size_display': main_nom,
|
||||
'size_display': size_display,
|
||||
'unit': 'EA'
|
||||
}
|
||||
|
||||
@@ -292,102 +385,198 @@ def generate_material_specs_for_category(materials: List[Dict], category: str) -
|
||||
flange_type = material.get('flange_type', 'FLANGE')
|
||||
pressure_rating = material.get('flange_pressure', '')
|
||||
material_spec = material.get('material_grade', '')
|
||||
main_nom = material.get('main_nom', '')
|
||||
# 상세 테이블의 사이즈 정보 우선 사용
|
||||
size_display = material.get('flange_size') or material.get('size_spec', '')
|
||||
|
||||
spec_parts = [flange_type]
|
||||
spec_parts = [flange_type.replace('_', ' ')]
|
||||
if pressure_rating: spec_parts.append(pressure_rating)
|
||||
full_spec = ', '.join(spec_parts)
|
||||
|
||||
spec_key = f"FLANGE|{full_spec}|{material_spec}|{main_nom}"
|
||||
spec_key = f"FLANGE|{full_spec}|{material_spec}|{size_display}"
|
||||
spec_data = {
|
||||
'category': 'FLANGE',
|
||||
'full_spec': full_spec,
|
||||
'material_spec': material_spec,
|
||||
'size_display': main_nom,
|
||||
'size_display': size_display,
|
||||
'unit': 'EA'
|
||||
}
|
||||
|
||||
elif category == 'BOLT':
|
||||
bolt_type = material.get('bolt_type', 'BOLT')
|
||||
material_standard = material.get('material_standard', '')
|
||||
diameter = material.get('diameter', material.get('main_nom', ''))
|
||||
# 상세 테이블의 사이즈 정보 우선 사용
|
||||
diameter = material.get('bolt_diameter') or material.get('size_spec', '')
|
||||
length = material.get('bolt_length', '')
|
||||
material_spec = material_standard or material.get('material_grade', '')
|
||||
|
||||
# 분수 사이즈 정보 추출 (새로 추가된 분류기 정보)
|
||||
size_fraction = material.get('size_fraction', diameter)
|
||||
surface_treatment = material.get('surface_treatment', '')
|
||||
# 기존 분류기 방식에 따른 사이즈 표시 (분수 형태)
|
||||
# 소수점을 분수로 변환 (예: 0.625 -> 5/8)
|
||||
size_display = diameter
|
||||
if diameter and '.' in diameter:
|
||||
try:
|
||||
decimal_val = float(diameter)
|
||||
# 일반적인 볼트 사이즈 분수 변환
|
||||
fraction_map = {
|
||||
0.25: "1/4\"", 0.3125: "5/16\"", 0.375: "3/8\"",
|
||||
0.4375: "7/16\"", 0.5: "1/2\"", 0.5625: "9/16\"",
|
||||
0.625: "5/8\"", 0.6875: "11/16\"", 0.75: "3/4\"",
|
||||
0.8125: "13/16\"", 0.875: "7/8\"", 1.0: "1\""
|
||||
}
|
||||
if decimal_val in fraction_map:
|
||||
size_display = fraction_map[decimal_val]
|
||||
except:
|
||||
pass
|
||||
|
||||
# 특수 용도 정보 추출 (PSV, LT, CK)
|
||||
special_applications = {
|
||||
'PSV': 0,
|
||||
'LT': 0,
|
||||
'CK': 0
|
||||
}
|
||||
|
||||
# 설명에서 특수 용도 키워드 확인 (간단한 방법)
|
||||
description = material.get('original_description', '').upper()
|
||||
if 'PSV' in description or 'PRESSURE SAFETY VALVE' in description:
|
||||
special_applications['PSV'] = material.get('quantity', 0)
|
||||
if any(keyword in description for keyword in ['LT', 'LOW TEMP', '저온용']):
|
||||
special_applications['LT'] = material.get('quantity', 0)
|
||||
if 'CK' in description or 'CHECK VALVE' in description:
|
||||
special_applications['CK'] = material.get('quantity', 0)
|
||||
# 길이 정보 포함한 사이즈 표시 (예: 5/8" x 165L)
|
||||
if length:
|
||||
# 길이에서 숫자만 추출
|
||||
import re
|
||||
length_match = re.search(r'(\d+(?:\.\d+)?)', str(length))
|
||||
if length_match:
|
||||
length_num = length_match.group(1)
|
||||
size_display_with_length = f"{size_display} x {length_num}L"
|
||||
else:
|
||||
size_display_with_length = f"{size_display} x {length}"
|
||||
else:
|
||||
size_display_with_length = size_display
|
||||
|
||||
spec_parts = [bolt_type.replace('_', ' ')]
|
||||
if material_standard: spec_parts.append(material_standard)
|
||||
full_spec = ', '.join(spec_parts)
|
||||
|
||||
# 특수 용도와 관계없이 사이즈+길이로 합산 (구매는 동일하므로)
|
||||
# 길이 정보가 있으면 포함
|
||||
length_info = material.get('length', '')
|
||||
if length_info:
|
||||
diameter_key = f"{diameter}L{length_info}"
|
||||
else:
|
||||
diameter_key = diameter
|
||||
|
||||
spec_key = f"BOLT|{full_spec}|{material_spec}|{diameter_key}"
|
||||
# 사이즈+길이로 그룹핑
|
||||
spec_key = f"BOLT|{full_spec}|{material_spec}|{size_display_with_length}"
|
||||
spec_data = {
|
||||
'category': 'BOLT',
|
||||
'full_spec': full_spec,
|
||||
'material_spec': material_spec,
|
||||
'size_display': diameter,
|
||||
'size_fraction': size_fraction,
|
||||
'surface_treatment': surface_treatment,
|
||||
'size_display': size_display_with_length,
|
||||
'unit': 'EA'
|
||||
}
|
||||
|
||||
elif category == 'GASKET':
|
||||
# 상세 테이블 정보 우선 사용
|
||||
gasket_type = material.get('gasket_type', 'GASKET')
|
||||
gasket_subtype = material.get('gasket_subtype', '')
|
||||
gasket_material = material.get('gasket_material', '')
|
||||
material_spec = gasket_material or material.get('material_grade', '')
|
||||
main_nom = material.get('main_nom', '')
|
||||
filler_material = material.get('filler_material', '')
|
||||
gasket_pressure = material.get('gasket_pressure', '')
|
||||
gasket_thickness = material.get('gasket_thickness', '')
|
||||
|
||||
# 상세 테이블의 사이즈 정보 우선 사용
|
||||
size_display = material.get('gasket_size') or material.get('size_spec', '')
|
||||
|
||||
# 기존 분류기 방식: 가스켓 타입 + 압력등급 + 재질
|
||||
# 예: "SPIRAL WOUND, 150LB, 304SS + GRAPHITE"
|
||||
spec_parts = [gasket_type.replace('_', ' ')]
|
||||
|
||||
# 서브타입 추가 (있는 경우)
|
||||
if gasket_subtype and gasket_subtype != gasket_type:
|
||||
spec_parts.append(gasket_subtype.replace('_', ' '))
|
||||
|
||||
# 상세 테이블의 압력등급 우선 사용, 없으면 description에서 추출
|
||||
if gasket_pressure:
|
||||
spec_parts.append(gasket_pressure)
|
||||
else:
|
||||
description = material.get('original_description', '').upper()
|
||||
import re
|
||||
pressure_match = re.search(r'(\d+)LB', description)
|
||||
if pressure_match:
|
||||
spec_parts.append(f"{pressure_match.group(1)}LB")
|
||||
|
||||
# 재질 정보 구성 (상세 테이블 정보 활용)
|
||||
material_spec_parts = []
|
||||
|
||||
# SWG의 경우 메탈 + 필러 형태로 구성
|
||||
if gasket_type == 'SPIRAL_WOUND':
|
||||
# 기존 저장된 데이터가 부정확한 경우, 원본 description에서 직접 파싱
|
||||
description = material.get('original_description', '').upper()
|
||||
|
||||
# SS304/GRAPHITE/CS/CS 패턴 파싱 (H/F/I/O 다음에 오는 재질 정보)
|
||||
import re
|
||||
material_spec = None
|
||||
|
||||
# H/F/I/O SS304/GRAPHITE/CS/CS 패턴 (전체 구성 표시)
|
||||
hfio_material_match = re.search(r'H/F/I/O\s+([A-Z0-9]+)/([A-Z]+)/([A-Z0-9]+)/([A-Z0-9]+)', description)
|
||||
if hfio_material_match:
|
||||
part1 = hfio_material_match.group(1) # SS304
|
||||
part2 = hfio_material_match.group(2) # GRAPHITE
|
||||
part3 = hfio_material_match.group(3) # CS
|
||||
part4 = hfio_material_match.group(4) # CS
|
||||
material_spec = f"{part1}/{part2}/{part3}/{part4}"
|
||||
else:
|
||||
# 단순 SS304/GRAPHITE/CS/CS 패턴 (전체 구성 표시)
|
||||
simple_material_match = re.search(r'([A-Z0-9]+)/([A-Z]+)/([A-Z0-9]+)/([A-Z0-9]+)', description)
|
||||
if simple_material_match:
|
||||
part1 = simple_material_match.group(1) # SS304
|
||||
part2 = simple_material_match.group(2) # GRAPHITE
|
||||
part3 = simple_material_match.group(3) # CS
|
||||
part4 = simple_material_match.group(4) # CS
|
||||
material_spec = f"{part1}/{part2}/{part3}/{part4}"
|
||||
|
||||
if not material_spec:
|
||||
# 상세 테이블 정보 사용
|
||||
if gasket_material and gasket_material != 'GRAPHITE': # 메탈 부분
|
||||
material_spec_parts.append(gasket_material)
|
||||
elif gasket_material == 'GRAPHITE':
|
||||
# GRAPHITE만 있는 경우 description에서 메탈 부분 찾기
|
||||
metal_match = re.search(r'(SS\d+|CS|INCONEL\d*)', description)
|
||||
if metal_match:
|
||||
material_spec_parts.append(metal_match.group(1))
|
||||
|
||||
if filler_material and filler_material != gasket_material: # 필러 부분
|
||||
material_spec_parts.append(filler_material)
|
||||
elif 'GRAPHITE' in description and 'GRAPHITE' not in material_spec_parts:
|
||||
material_spec_parts.append('GRAPHITE')
|
||||
|
||||
if material_spec_parts:
|
||||
material_spec = ' + '.join(material_spec_parts) # SS304 + GRAPHITE
|
||||
else:
|
||||
material_spec = material.get('material_grade', '')
|
||||
else:
|
||||
# 일반 가스켓의 경우
|
||||
if gasket_material:
|
||||
material_spec_parts.append(gasket_material)
|
||||
if filler_material and filler_material != gasket_material:
|
||||
material_spec_parts.append(filler_material)
|
||||
|
||||
if material_spec_parts:
|
||||
material_spec = ', '.join(material_spec_parts)
|
||||
else:
|
||||
material_spec = material.get('material_grade', '')
|
||||
|
||||
if material_spec:
|
||||
spec_parts.append(material_spec)
|
||||
|
||||
# 두께 정보 추가 (있는 경우)
|
||||
if gasket_thickness:
|
||||
spec_parts.append(f"THK {gasket_thickness}")
|
||||
|
||||
spec_parts = [gasket_type]
|
||||
if gasket_material: spec_parts.append(gasket_material)
|
||||
full_spec = ', '.join(spec_parts)
|
||||
|
||||
spec_key = f"GASKET|{full_spec}|{material_spec}|{main_nom}"
|
||||
spec_key = f"GASKET|{full_spec}|{material_spec}|{size_display}"
|
||||
spec_data = {
|
||||
'category': 'GASKET',
|
||||
'full_spec': full_spec,
|
||||
'material_spec': material_spec,
|
||||
'size_display': main_nom,
|
||||
'size_display': size_display,
|
||||
'unit': 'EA'
|
||||
}
|
||||
|
||||
elif category == 'INSTRUMENT':
|
||||
instrument_type = material.get('instrument_type', 'INSTRUMENT')
|
||||
material_spec = material.get('material_grade', '')
|
||||
main_nom = material.get('main_nom', '')
|
||||
# 상세 테이블의 사이즈 정보 우선 사용
|
||||
size_display = material.get('instrument_size') or material.get('size_spec', '')
|
||||
|
||||
full_spec = instrument_type.replace('_', ' ')
|
||||
|
||||
spec_key = f"INSTRUMENT|{full_spec}|{material_spec}|{main_nom}"
|
||||
spec_key = f"INSTRUMENT|{full_spec}|{material_spec}|{size_display}"
|
||||
spec_data = {
|
||||
'category': 'INSTRUMENT',
|
||||
'full_spec': full_spec,
|
||||
'material_spec': material_spec,
|
||||
'size_display': main_nom,
|
||||
'size_display': size_display,
|
||||
'unit': 'EA'
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user