feat: 리비전 관리 시스템 및 구매확정 기능 구현

- 리비전 관리 라우터 및 서비스 추가 (revision_management.py, revision_comparison_service.py, revision_session_service.py)
- 구매확정 기능 구현: materials 테이블에 purchase_confirmed 필드 추가 및 업데이트 로직
- 리비전 비교 로직 구현: 구매확정된 자재 기반으로 신규/변경 자재 자동 분류
- 데이터베이스 스키마 확장: revision_sessions, revision_material_changes, inventory_transfers 테이블 추가
- 구매신청 생성 시 자재 상세 정보 저장 및 purchase_confirmed 자동 업데이트
- 프론트엔드: 리비전 관리 컴포넌트 및 hooks 추가
- 파일 목록 조회 API 추가 (/files/list)

🤖 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 07:36:44 +09:00
parent c258303bb7
commit 17843e285f
12 changed files with 2759 additions and 83 deletions

View File

@@ -42,6 +42,14 @@ async def create_purchase_request(
구매신청 생성 (엑셀 내보내기 = 구매신청)
"""
try:
# 🔍 디버깅: 요청 데이터 로깅
logger.info(f"🔍 구매신청 생성 요청 - file_id: {request_data.file_id}, job_no: {request_data.job_no}")
logger.info(f"🔍 material_ids 개수: {len(request_data.material_ids)}")
logger.info(f"🔍 materials_data 개수: {len(request_data.materials_data)}")
if request_data.material_ids:
logger.info(f"🔍 material_ids 샘플: {request_data.material_ids[:5]}")
print(f"🔍 [DEBUG] 구매신청 API 호출됨 - material_ids: {len(request_data.material_ids)}")
# 구매신청 번호 생성
today = datetime.now().strftime('%Y%m%d')
count_query = text("""
@@ -116,9 +124,13 @@ async def create_purchase_request(
if not existing:
insert_item = text("""
INSERT INTO purchase_request_items (
request_id, material_id, quantity, unit, user_requirement
request_id, material_id, description, category, subcategory,
material_grade, size_spec, quantity, unit, drawing_name,
notes, user_requirement
) VALUES (
:request_id, :material_id, :quantity, :unit, :user_requirement
:request_id, :material_id, :description, :category, :subcategory,
:material_grade, :size_spec, :quantity, :unit, :drawing_name,
:notes, :user_requirement
)
""")
# quantity를 정수로 변환 (소수점 제거)
@@ -131,14 +143,44 @@ async def create_purchase_request(
db.execute(insert_item, {
"request_id": request_id,
"material_id": material_id,
"description": material_data.get("description", material_data.get("original_description", "")),
"category": material_data.get("category", material_data.get("classified_category", "")),
"subcategory": material_data.get("subcategory", material_data.get("classified_subcategory", "")),
"material_grade": material_data.get("material_grade", ""),
"size_spec": material_data.get("size_spec", ""),
"quantity": quantity,
"unit": material_data.get("unit", ""),
"unit": material_data.get("unit", "EA"),
"drawing_name": material_data.get("drawing_name", ""),
"notes": material_data.get("notes", ""),
"user_requirement": material_data.get("user_requirement", "")
})
inserted_count += 1
else:
logger.warning(f"Material {material_id} already in another purchase request, skipping")
# 🔥 중요: materials 테이블의 purchase_confirmed 업데이트
if request_data.material_ids:
print(f"🔥 [PURCHASE] purchase_confirmed 업데이트 시작: {len(request_data.material_ids)}개 자재")
print(f"🔥 [PURCHASE] material_ids: {request_data.material_ids[:5]}...") # 처음 5개만 로그
update_materials_query = text("""
UPDATE materials
SET purchase_confirmed = true,
purchase_confirmed_at = NOW(),
purchase_confirmed_by = :confirmed_by
WHERE id = ANY(:material_ids)
""")
result = db.execute(update_materials_query, {
"material_ids": request_data.material_ids,
"confirmed_by": current_user.get("username", "system")
})
print(f"🔥 [PURCHASE] UPDATE 결과: {result.rowcount}개 행 업데이트됨")
logger.info(f"{len(request_data.material_ids)}개 자재의 purchase_confirmed를 true로 업데이트")
else:
print(f"⚠️ [PURCHASE] material_ids가 비어있음!")
db.commit()
logger.info(f"Purchase request created: {request_no} with {inserted_count} materials (out of {len(request_data.material_ids)} requested)")
@@ -150,6 +192,20 @@ async def create_purchase_request(
verified_count = db.execute(verify_query, {"request_id": request_id}).fetchone().count
logger.info(f"✅ DB 검증: purchase_request_items에 {verified_count}개 저장됨")
# purchase_requests 테이블의 total_items 필드 업데이트
update_total_items = text("""
UPDATE purchase_requests
SET total_items = :total_items
WHERE request_id = :request_id
""")
db.execute(update_total_items, {
"request_id": request_id,
"total_items": verified_count
})
db.commit()
logger.info(f"✅ total_items 업데이트 완료: {verified_count}")
return {
"success": True,
"request_no": request_no,
@@ -224,7 +280,7 @@ async def get_purchase_requests(
"job_no": row.job_no,
"job_name": row.job_name,
"category": "ALL", # 기본값
"material_count": row.total_items or 0,
"material_count": row.item_count or 0, # 실제 자재 개수 사용
"item_count": row.item_count,
"excel_file_path": None, # 현재 테이블에 없음
"requested_at": row.request_date.isoformat() if row.request_date else None,