fix: 리비전 업로드 시 구매확정 상태 상속 문제 해결

문제점:
- 리비전 파일 업로드 시 모든 자재가 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 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2025-12-06 09:13:08 +09:00
parent a6868b129e
commit 6ad1ef7aad

View File

@@ -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)
}