feat: 자재 관리 페이지 대규모 개선
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
- 파이프 수량 계산 로직 수정 (단관 개수가 아닌 실제 길이 기반 계산) - UI 전면 개편 (DevonThink 스타일의 간결하고 세련된 디자인) - 자재별 그룹핑 로직 개선: * 플랜지: 동일 사양별 그룹핑, WN 스케줄 표시, ORIFICE 풀네임 표시 * 피팅: 상세 타입 표시 (니플 길이, 엘보 각도/연결, 티 타입, 리듀서 타입 등) * 밸브: 동일 사양별 그룹핑, 타입/연결방식/압력 표시 * 볼트: 크기/재질/길이별 그룹핑 (8SET → 개별 집계) * 가스켓: 동일 사양별 그룹핑, 재질/상세내역/두께 분리 표시 * UNKNOWN: 원본 설명 전체 표시, 동일 항목 그룹핑 - 전체 카테고리 버튼 제거 (표시 복잡도 감소) - 카테고리별 동적 컬럼 헤더 및 레이아웃 적용
This commit is contained in:
@@ -157,6 +157,26 @@ async def confirm_material_purchase(
|
||||
]
|
||||
"""
|
||||
try:
|
||||
# 입력 데이터 검증
|
||||
if not job_no or not revision:
|
||||
raise HTTPException(status_code=400, detail="Job 번호와 리비전은 필수입니다")
|
||||
|
||||
if not confirmations:
|
||||
raise HTTPException(status_code=400, detail="확정할 자재가 없습니다")
|
||||
|
||||
# 각 확정 항목 검증
|
||||
for i, confirmation in enumerate(confirmations):
|
||||
if not confirmation.get("material_hash"):
|
||||
raise HTTPException(status_code=400, detail=f"{i+1}번째 항목의 material_hash가 없습니다")
|
||||
|
||||
confirmed_qty = confirmation.get("confirmed_quantity")
|
||||
if confirmed_qty is None or confirmed_qty < 0:
|
||||
raise HTTPException(status_code=400, detail=f"{i+1}번째 항목의 확정 수량이 유효하지 않습니다")
|
||||
|
||||
unit_price = confirmation.get("unit_price", 0)
|
||||
if unit_price < 0:
|
||||
raise HTTPException(status_code=400, detail=f"{i+1}번째 항목의 단가가 유효하지 않습니다")
|
||||
|
||||
confirmed_items = []
|
||||
|
||||
for confirmation in confirmations:
|
||||
@@ -470,7 +490,7 @@ async def get_materials_by_hash(db: Session, file_id: int) -> Dict[str, Dict]:
|
||||
"""파일의 자재를 해시별로 그룹화하여 조회"""
|
||||
import hashlib
|
||||
|
||||
print(f"🚨🚨🚨 get_materials_by_hash 호출됨! file_id={file_id} 🚨🚨🚨")
|
||||
# 로그 제거
|
||||
|
||||
query = text("""
|
||||
SELECT
|
||||
@@ -492,11 +512,7 @@ async def get_materials_by_hash(db: Session, file_id: int) -> Dict[str, Dict]:
|
||||
result = db.execute(query, {"file_id": file_id})
|
||||
materials = result.fetchall()
|
||||
|
||||
print(f"🔍 쿼리 결과 개수: {len(materials)}")
|
||||
if len(materials) > 0:
|
||||
print(f"🔍 첫 번째 자료 샘플: {materials[0]}")
|
||||
else:
|
||||
print(f"❌ 자료가 없음! file_id={file_id}")
|
||||
# 로그 제거
|
||||
|
||||
# 🔄 같은 파이프들을 Python에서 올바르게 그룹핑
|
||||
materials_dict = {}
|
||||
@@ -505,38 +521,41 @@ async def get_materials_by_hash(db: Session, file_id: int) -> Dict[str, Dict]:
|
||||
hash_source = f"{mat[1] or ''}|{mat[2] or ''}|{mat[3] or ''}"
|
||||
material_hash = hashlib.md5(hash_source.encode()).hexdigest()
|
||||
|
||||
print(f"📝 개별 자재: {mat[1][:50]}... ({mat[2]}) - 수량: {mat[4]}, 길이: {mat[7]}mm")
|
||||
# 개별 자재 로그 제거 (너무 많음)
|
||||
|
||||
if material_hash in materials_dict:
|
||||
# 🔄 기존 항목에 수량 합계
|
||||
existing = materials_dict[material_hash]
|
||||
existing["quantity"] += float(mat[4]) if mat[4] else 0.0
|
||||
# 파이프가 아닌 경우만 quantity 합산 (파이프는 개별 길이가 다르므로 합산하지 않음)
|
||||
if mat[5] != 'PIPE':
|
||||
existing["quantity"] += float(mat[4]) if mat[4] else 0.0
|
||||
existing["line_number"] += f", {mat[8]}" if mat[8] else ""
|
||||
|
||||
# 파이프인 경우 길이 정보 합산
|
||||
if mat[5] == 'PIPE' and mat[7] is not None:
|
||||
if "pipe_details" in existing:
|
||||
# 총길이 합산: 기존 총길이 + (현재 수량 × 현재 길이)
|
||||
# 총길이 합산: 기존 총길이 + 현재 파이프의 실제 길이 (DB에 저장된 개별 길이)
|
||||
current_total = existing["pipe_details"]["total_length_mm"]
|
||||
current_count = existing["pipe_details"]["pipe_count"]
|
||||
|
||||
new_length = float(mat[4]) * float(mat[7]) # 수량 × 단위길이
|
||||
existing["pipe_details"]["total_length_mm"] = current_total + new_length
|
||||
existing["pipe_details"]["pipe_count"] = current_count + float(mat[4])
|
||||
# ✅ DB에서 가져온 length_mm는 이미 개별 파이프의 실제 길이이므로 수량을 곱하지 않음
|
||||
individual_length = float(mat[7]) # 개별 파이프의 실제 길이
|
||||
existing["pipe_details"]["total_length_mm"] = current_total + individual_length
|
||||
existing["pipe_details"]["pipe_count"] = current_count + 1 # 파이프 개수는 1개씩 증가
|
||||
|
||||
# 평균 단위 길이 재계산
|
||||
total_length = existing["pipe_details"]["total_length_mm"]
|
||||
total_count = existing["pipe_details"]["pipe_count"]
|
||||
existing["pipe_details"]["length_mm"] = total_length / total_count
|
||||
|
||||
print(f"🔄 파이프 합산: {mat[1]} ({mat[2]}) - 총길이: {total_length}mm, 총개수: {total_count}개, 평균: {total_length/total_count:.1f}mm")
|
||||
# 파이프 합산 로그 제거 (너무 많음)
|
||||
else:
|
||||
# 첫 파이프 정보 설정
|
||||
pipe_length = float(mat[4]) * float(mat[7])
|
||||
individual_length = float(mat[7]) # 개별 파이프의 실제 길이
|
||||
existing["pipe_details"] = {
|
||||
"length_mm": float(mat[7]),
|
||||
"total_length_mm": pipe_length,
|
||||
"pipe_count": float(mat[4])
|
||||
"length_mm": individual_length,
|
||||
"total_length_mm": individual_length, # 첫 번째 파이프이므로 개별 길이와 동일
|
||||
"pipe_count": 1 # 첫 번째 파이프이므로 1개
|
||||
}
|
||||
else:
|
||||
# 🆕 새 항목 생성
|
||||
@@ -553,27 +572,22 @@ async def get_materials_by_hash(db: Session, file_id: int) -> Dict[str, Dict]:
|
||||
|
||||
# 파이프인 경우 pipe_details 정보 추가
|
||||
if mat[5] == 'PIPE' and mat[7] is not None:
|
||||
pipe_length = float(mat[4]) * float(mat[7]) # 수량 × 단위길이
|
||||
individual_length = float(mat[7]) # 개별 파이프의 실제 길이
|
||||
material_data["pipe_details"] = {
|
||||
"length_mm": float(mat[7]), # 단위 길이
|
||||
"total_length_mm": pipe_length, # 총 길이
|
||||
"pipe_count": float(mat[4]) # 파이프 개수
|
||||
"length_mm": individual_length, # 개별 파이프 길이
|
||||
"total_length_mm": individual_length, # 첫 번째 파이프이므로 개별 길이와 동일
|
||||
"pipe_count": 1 # 첫 번째 파이프이므로 1개
|
||||
}
|
||||
print(f"🆕 파이프 신규: {mat[1]} ({mat[2]}) - 단위: {mat[7]}mm, 총길이: {pipe_length}mm")
|
||||
# 파이프는 quantity를 1로 설정 (pipe_count와 동일)
|
||||
material_data["quantity"] = 1
|
||||
|
||||
materials_dict[material_hash] = material_data
|
||||
|
||||
# 파이프 데이터가 포함되었는지 확인
|
||||
# 파이프 데이터 요약만 출력
|
||||
pipe_count = sum(1 for data in materials_dict.values() if data.get('category') == 'PIPE')
|
||||
pipe_with_details = sum(1 for data in materials_dict.values()
|
||||
if data.get('category') == 'PIPE' and 'pipe_details' in data)
|
||||
print(f"🔍 반환 결과: 총 {len(materials_dict)}개 자재, 파이프 {pipe_count}개, pipe_details 있는 파이프 {pipe_with_details}개")
|
||||
|
||||
# 첫 번째 파이프 데이터 샘플 출력
|
||||
for hash_key, data in materials_dict.items():
|
||||
if data.get('category') == 'PIPE':
|
||||
print(f"🔍 파이프 샘플: {data}")
|
||||
break
|
||||
print(f"✅ 자재 처리 완료: 총 {len(materials_dict)}개, 파이프 {pipe_count}개 (길이정보: {pipe_with_details}개)")
|
||||
|
||||
return materials_dict
|
||||
|
||||
|
||||
Reference in New Issue
Block a user