feat: 구매신청 기능 완성 및 SUPPORT/SPECIAL 카테고리 개선
- 모든 카테고리 구매신청 기능 완성 (PIPE, FITTING, VALVE, FLANGE, GASKET, BOLT, SUPPORT, SPECIAL, UNKNOWN) - 구매신청 완료 항목: 회색 배경, 체크박스 비활성화, '구매신청완료' 배지 표시 - 전체 선택/구매신청 시 이미 구매신청된 항목 자동 제외 - 구매신청 quantity 타입 에러 수정 (문자열 -> 정수 변환) SUPPORT 카테고리 (구 U-BOLT): - U-BOLT -> SUPPORT로 카테고리명 변경 - 클램프, 유볼트, 우레탄블럭슈 분류 개선 - 테이블 헤더: 선택-종류-타입-크기-디스크립션-추가요구-사용자요구-수량 - 크기 정보 main_nom 필드에서 가져오기 (배관 인치) - 엑셀 내보내기 형식 조정 SPECIAL 카테고리: - SPECIAL 키워드 자재 자동 분류 (SPECIFICATION 제외) - 파일 업로드 시 SPECIAL 카테고리 처리 로직 추가 - 도면번호 필드 추가 (drawing_name, line_no) - 타입 필드: 크기/스케줄/재질 제외한 핵심 정보 표시 - 엑셀 DWG_NAME, LINE_NUM 컬럼 파싱 및 저장 FITTING 카테고리: - 테이블 컬럼 너비 조정 (선택 2%, 종류 8.5%, 수량 12%) 구매신청 관리: - 엑셀 재다운로드 형식 개선 (BOM 페이지와 동일한 형식) - 그룹화된 자재 정보 포함하여 저장 및 다운로드
This commit is contained in:
@@ -218,7 +218,8 @@ def parse_dataframe(df):
|
||||
mapped_columns[standard_col] = possible_name
|
||||
break
|
||||
|
||||
# 로그 제거
|
||||
print(f"📋 엑셀 컬럼 매핑 결과: {mapped_columns}")
|
||||
print(f"📋 원본 컬럼명들: {list(df.columns)}")
|
||||
|
||||
materials = []
|
||||
for index, row in df.iterrows():
|
||||
@@ -262,16 +263,34 @@ def parse_dataframe(df):
|
||||
except (ValueError, TypeError):
|
||||
length_value = None
|
||||
|
||||
# DWG_NAME 정보 추출
|
||||
dwg_name_raw = row.get(mapped_columns.get('dwg_name', ''), '')
|
||||
dwg_name = None
|
||||
if pd.notna(dwg_name_raw) and str(dwg_name_raw).strip() not in ['', 'nan', 'None']:
|
||||
dwg_name = str(dwg_name_raw).strip()
|
||||
if index < 3: # 처음 3개만 로그
|
||||
print(f"📐 도면번호 파싱: {dwg_name}")
|
||||
|
||||
# LINE_NUM 정보 추출
|
||||
line_num_raw = row.get(mapped_columns.get('line_num', ''), '')
|
||||
line_num = None
|
||||
if pd.notna(line_num_raw) and str(line_num_raw).strip() not in ['', 'nan', 'None']:
|
||||
line_num = str(line_num_raw).strip()
|
||||
if index < 3: # 처음 3개만 로그
|
||||
print(f"📍 라인번호 파싱: {line_num}")
|
||||
|
||||
if description and description not in ['nan', 'None', '']:
|
||||
materials.append({
|
||||
'original_description': description,
|
||||
'quantity': quantity,
|
||||
'unit': "EA",
|
||||
'size_spec': size_spec,
|
||||
'main_nom': main_nom, # 추가
|
||||
'red_nom': red_nom, # 추가
|
||||
'main_nom': main_nom,
|
||||
'red_nom': red_nom,
|
||||
'material_grade': material_grade,
|
||||
'length': length_value,
|
||||
'dwg_name': dwg_name,
|
||||
'line_num': line_num,
|
||||
'line_number': index + 1,
|
||||
'row_number': index + 1
|
||||
})
|
||||
@@ -651,6 +670,18 @@ async def upload_file(
|
||||
elif material_type == "SUPPORT":
|
||||
from ..services.support_classifier import classify_support
|
||||
classification_result = classify_support("", description, main_nom or "")
|
||||
elif material_type == "SPECIAL":
|
||||
# SPECIAL 카테고리는 별도 분류기 없이 통합 분류 결과 사용
|
||||
classification_result = {
|
||||
"category": "SPECIAL",
|
||||
"overall_confidence": integrated_result.get('confidence', 1.0),
|
||||
"reason": integrated_result.get('reason', 'SPECIAL 키워드 발견'),
|
||||
"details": {
|
||||
"description": description,
|
||||
"main_nom": main_nom or "",
|
||||
"drawing_required": True # 도면 필요
|
||||
}
|
||||
}
|
||||
else:
|
||||
# UNKNOWN 처리
|
||||
classification_result = {
|
||||
@@ -679,12 +710,14 @@ async def upload_file(
|
||||
INSERT INTO materials (
|
||||
file_id, original_description, quantity, unit, size_spec,
|
||||
main_nom, red_nom, material_grade, full_material_grade, line_number, row_number,
|
||||
classified_category, classification_confidence, is_verified, created_at
|
||||
classified_category, classification_confidence, is_verified,
|
||||
drawing_name, line_no, created_at
|
||||
)
|
||||
VALUES (
|
||||
:file_id, :original_description, :quantity, :unit, :size_spec,
|
||||
:main_nom, :red_nom, :material_grade, :full_material_grade, :line_number, :row_number,
|
||||
:classified_category, :classification_confidence, :is_verified, :created_at
|
||||
:classified_category, :classification_confidence, :is_verified,
|
||||
:drawing_name, :line_no, :created_at
|
||||
)
|
||||
RETURNING id
|
||||
""")
|
||||
@@ -702,8 +735,8 @@ async def upload_file(
|
||||
"quantity": material_data["quantity"],
|
||||
"unit": material_data["unit"],
|
||||
"size_spec": material_data["size_spec"],
|
||||
"main_nom": material_data.get("main_nom"), # 추가
|
||||
"red_nom": material_data.get("red_nom"), # 추가
|
||||
"main_nom": material_data.get("main_nom"),
|
||||
"red_nom": material_data.get("red_nom"),
|
||||
"material_grade": material_data["material_grade"],
|
||||
"full_material_grade": full_material_grade,
|
||||
"line_number": material_data["line_number"],
|
||||
@@ -711,6 +744,8 @@ async def upload_file(
|
||||
"classified_category": classification_result.get("category", "UNKNOWN"),
|
||||
"classification_confidence": classification_result.get("overall_confidence", 0.0),
|
||||
"is_verified": False,
|
||||
"drawing_name": material_data.get("dwg_name"),
|
||||
"line_no": material_data.get("line_num"),
|
||||
"created_at": datetime.now()
|
||||
})
|
||||
|
||||
@@ -1565,6 +1600,8 @@ async def get_materials(
|
||||
size_spec: Optional[str] = None,
|
||||
file_filter: Optional[str] = None,
|
||||
sort_by: Optional[str] = None,
|
||||
exclude_requested: bool = True, # 구매신청된 자재 제외 여부
|
||||
group_by_spec: bool = False, # 같은 사양끼리 그룹화
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
@@ -1575,6 +1612,7 @@ async def get_materials(
|
||||
query = """
|
||||
SELECT m.id, m.file_id, m.original_description, m.quantity, m.unit,
|
||||
m.size_spec, m.main_nom, m.red_nom, m.material_grade, m.full_material_grade, m.line_number, m.row_number,
|
||||
m.drawing_name, m.line_no,
|
||||
m.created_at, m.classified_category, m.classification_confidence,
|
||||
m.classification_details,
|
||||
m.is_verified, m.verified_by, m.verified_at,
|
||||
@@ -1612,6 +1650,8 @@ async def get_materials(
|
||||
FROM materials m
|
||||
LEFT JOIN files f ON m.file_id = f.id
|
||||
LEFT JOIN projects p ON f.project_id = p.id
|
||||
-- 구매신청된 자재 제외
|
||||
LEFT JOIN purchase_request_items pri ON m.id = pri.material_id
|
||||
LEFT JOIN pipe_details pd ON m.id = pd.material_id
|
||||
LEFT JOIN pipe_end_preparations pep ON m.id = pep.material_id
|
||||
LEFT JOIN fitting_details fd ON m.id = fd.material_id
|
||||
@@ -1625,6 +1665,11 @@ async def get_materials(
|
||||
WHERE 1=1
|
||||
"""
|
||||
params = {}
|
||||
|
||||
# 구매신청된 자재 제외
|
||||
if exclude_requested:
|
||||
query += " AND pri.material_id IS NULL"
|
||||
|
||||
if project_id:
|
||||
query += " AND f.project_id = :project_id"
|
||||
params["project_id"] = project_id
|
||||
@@ -1769,11 +1814,13 @@ async def get_materials(
|
||||
"quantity": float(m.quantity) if m.quantity else 0,
|
||||
"unit": m.unit,
|
||||
"size_spec": m.size_spec,
|
||||
"main_nom": m.main_nom, # 추가
|
||||
"red_nom": m.red_nom, # 추가
|
||||
"material_grade": m.full_material_grade or enhanced_material_grade, # 전체 재질명 우선 사용
|
||||
"original_material_grade": m.material_grade, # 원본 재질 정보도 보존
|
||||
"full_material_grade": m.full_material_grade, # 전체 재질명
|
||||
"main_nom": m.main_nom,
|
||||
"red_nom": m.red_nom,
|
||||
"material_grade": m.full_material_grade or enhanced_material_grade,
|
||||
"original_material_grade": m.material_grade,
|
||||
"full_material_grade": m.full_material_grade,
|
||||
"drawing_name": m.drawing_name,
|
||||
"line_no": m.line_no,
|
||||
"line_number": m.line_number,
|
||||
"row_number": m.row_number,
|
||||
# 구매수량 계산에서 분류된 정보를 우선 사용
|
||||
@@ -2093,6 +2140,20 @@ async def get_materials(
|
||||
# 평균 단위 길이 계산
|
||||
if group_info["total_quantity"] > 0:
|
||||
representative_pipe['pipe_details']['avg_length_mm'] = group_info["total_length_mm"] / group_info["total_quantity"]
|
||||
|
||||
# 개별 파이프 길이 정보 수집
|
||||
individual_pipes = []
|
||||
for mat in group_info["materials"]:
|
||||
if 'pipe_details' in mat and mat['pipe_details'].get('length_mm'):
|
||||
individual_pipes.append({
|
||||
'length': mat['pipe_details']['length_mm'],
|
||||
'quantity': 1,
|
||||
'id': mat['id']
|
||||
})
|
||||
representative_pipe['pipe_details']['individual_pipes'] = individual_pipes
|
||||
|
||||
# 그룹화된 모든 자재 ID 저장
|
||||
representative_pipe['grouped_ids'] = [mat['id'] for mat in group_info["materials"]]
|
||||
|
||||
material_list.append(representative_pipe)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user