From 6ad1ef7aad0d8cab500c10ae3c2d7ae07f4fa357 Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Sat, 6 Dec 2025 09:13:08 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B9=84=EC=A0=84=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EC=8B=9C=20=EA=B5=AC=EB=A7=A4=ED=99=95?= =?UTF-8?q?=EC=A0=95=20=EC=83=81=ED=83=9C=20=EC=83=81=EC=86=8D=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 문제점: - 리비전 파일 업로드 시 모든 자재가 purchase_confirmed=false로 새로 저장됨 - 이전 리비전에서 구매확정한 자재가 다시 미구매 상태로 돌아가는 버그 해결방법: 1. perform_simple_revision_comparison 함수에서 구매확정 정보 반환 - purchase_confirmed, purchase_confirmed_at, purchase_confirmed_by - purchased_materials_map 딕셔너리로 반환 2. materials 테이블 insert 시 구매확정 상태 상속 - 자재 식별 키로 purchased_materials_map 확인 - 매칭되면 구매확정 상태와 메타데이터 상속 - 로그 출력: "🔥 구매확정 상태 상속: ..." 3. 디버깅 정보 개선 - 리비전 비교 결과에 excluded_purchased_count 추가 - 첫 번째 자재 저장 시 purchase_confirmed 상태 출력 동작 방식: 1. Rev.1에서 자재 A를 구매확정 → purchase_confirmed=true 2. Rev.2 업로드 시 자재 A가 포함되어 있으면 3. 리비전 비교에서 자재 A를 purchased_materials_map에 저장 4. 새 파일의 자재 A 저장 시 구매확정 상태 상속 5. Rev.2의 자재 A도 purchase_confirmed=true 유지 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- backend/app/routers/files.py | 63 +++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/backend/app/routers/files.py b/backend/app/routers/files.py index 85dd770..1f04a55 100644 --- a/backend/app/routers/files.py +++ b/backend/app/routers/files.py @@ -514,7 +514,8 @@ async def upload_file( # 🔄 리비전 비교 수행 (RULES.md 코딩 컨벤션 준수) revision_comparison = None materials_to_classify = materials_data - + purchased_materials_map = {} # 구매확정된 자재 매핑 (키 -> 구매확정 정보) + if revision != "Rev.0": # 리비전 업로드인 경우만 비교 print(f"🔍 [DEBUG] 리비전 비교 시작 - revision: {revision}, parent_file_id: {parent_file_id}") try: @@ -522,20 +523,24 @@ async def upload_file( print(f"🔍 [DEBUG] perform_simple_revision_comparison 호출 중...") revision_comparison = perform_simple_revision_comparison(db, job_no, parent_file_id, materials_data) print(f"🔍 [DEBUG] 리비전 비교 완료: {revision_comparison.keys() if revision_comparison else 'None'}") - + if revision_comparison.get("has_purchased_materials", False): print(f"📊 간단한 리비전 비교 결과:") print(f" - 구매확정된 자재: {revision_comparison.get('purchased_count', 0)}개") print(f" - 미구매 자재: {revision_comparison.get('unpurchased_count', 0)}개") print(f" - 신규 자재: {revision_comparison.get('new_count', 0)}개") - print(f" - 변경된 자재: {revision_comparison.get('changed_count', 0)}개") - + print(f" - 제외된 구매확정 자재: {revision_comparison.get('excluded_purchased_count', 0)}개") + # 신규 및 변경된 자재만 분류 materials_to_classify = revision_comparison.get("materials_to_classify", []) print(f" - 분류 필요: {len(materials_to_classify)}개") + + # 🔥 구매확정된 자재 매핑 정보 저장 + purchased_materials_map = revision_comparison.get("purchased_materials_map", {}) + print(f" - 구매확정 자재 매핑: {len(purchased_materials_map)}개") else: print("📝 이전 구매확정 자료 없음 - 전체 자재 분류") - + except Exception as e: print(f"⚠️ 리비전 비교 실패, 전체 자재 분류로 진행: {str(e)}") import traceback @@ -752,23 +757,42 @@ async def upload_file( if not full_material_grade and material_data.get("material_grade"): full_material_grade = material_data["material_grade"] + # 🔥 구매확정 상태 확인 (리비전인 경우) + is_purchase_confirmed = False + purchase_confirmed_at = None + purchase_confirmed_by = None + + if parent_file_id and purchased_materials_map: + # 자재 식별 키 생성 (리비전 비교와 동일한 방식) + material_key = f"{description.strip().upper()}|{size_spec or ''}" + + if material_key in purchased_materials_map: + # 이전 리비전에서 구매확정된 자재 + purchased_info = purchased_materials_map[material_key] + is_purchase_confirmed = True + purchase_confirmed_at = purchased_info.get("purchase_confirmed_at") + purchase_confirmed_by = purchased_info.get("purchase_confirmed_by") + print(f"🔥 구매확정 상태 상속: {description[:50]}...") + # 기본 자재 정보 저장 material_insert_query = text(""" 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, - drawing_name, line_no, created_at + 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, + drawing_name, line_no, created_at, + purchase_confirmed, purchase_confirmed_at, purchase_confirmed_by ) 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, - :drawing_name, :line_no, :created_at + :drawing_name, :line_no, :created_at, + :purchase_confirmed, :purchase_confirmed_at, :purchase_confirmed_by ) RETURNING id """) - + # 첫 번째 자재에 대해서만 디버그 출력 if materials_inserted == 0: print(f"첫 번째 자재 저장:") @@ -777,7 +801,8 @@ async def upload_file( print(f" category: {classification_result.get('category', 'UNCLASSIFIED')}") print(f" drawing_name: {material_data.get('dwg_name')}") print(f" line_no: {material_data.get('line_num')}") - + print(f" purchase_confirmed: {is_purchase_confirmed}") + material_result = db.execute(material_insert_query, { "file_id": file_id, "original_description": material_data["original_description"], @@ -795,7 +820,10 @@ async def upload_file( "is_verified": False, "drawing_name": material_data.get("dwg_name"), "line_no": material_data.get("line_num"), - "created_at": datetime.now() + "created_at": datetime.now(), + "purchase_confirmed": is_purchase_confirmed, + "purchase_confirmed_at": purchase_confirmed_at, + "purchase_confirmed_by": purchase_confirmed_by }) material_id = material_result.fetchone()[0] @@ -4066,12 +4094,12 @@ def perform_simple_revision_comparison(db: Session, job_no: str, parent_file_id: } """ try: - # 1. 이전 파일의 모든 자재 조회 + # 1. 이전 파일의 모든 자재 조회 (구매확정 정보 포함) previous_materials_query = text(""" SELECT original_description, classified_category, size_spec, material_grade, main_nom, red_nom, drawing_name, line_no, COALESCE(total_quantity, quantity, 0) as quantity, unit, - purchase_confirmed + purchase_confirmed, purchase_confirmed_at, purchase_confirmed_by FROM materials WHERE file_id = :parent_file_id ORDER BY id @@ -4111,7 +4139,9 @@ def perform_simple_revision_comparison(db: Session, job_no: str, parent_file_id: "line_no": material.line_no, "quantity": float(material.quantity or 0), "unit": material.unit, - "purchase_confirmed": material.purchase_confirmed + "purchase_confirmed": material.purchase_confirmed, + "purchase_confirmed_at": material.purchase_confirmed_at, + "purchase_confirmed_by": material.purchase_confirmed_by } if key in previous_dict: @@ -4216,6 +4246,7 @@ def perform_simple_revision_comparison(db: Session, job_no: str, parent_file_id: "excluded_purchased_count": excluded_purchased_count, "materials_to_classify": materials_to_classify, # 신규 자재만 "removed_materials": removed_materials, # 삭제된 자재 + "purchased_materials_map": purchased_dict, # 🔥 구매확정된 자재 매핑 정보 "total_previous": len(previous_dict), "total_new": len(new_dict) }