[TEST] Cloudflare Tunnel 대응 및 리비전 증분 계산 수정
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
🌐 Nginx 프록시 설정 (테스트용): - nginx-proxy.conf: /api 요청을 백엔드로 프록시 - docker-compose.proxy.yml: 프록시 서버 설정 - VITE_API_URL=/api 환경변수 설정으로 단일 도메인 접속 🎨 UI 텍스트 변경 (테스트용): - LoginPage: TK-MP System → BOM 테스트 서버 - LoginPage: 통합 프로젝트 관리 시스템 → BOM 분류 시스템 v1.0 - LogMonitoringPage: 탭 네비게이션 추가 (로그인/활동/시스템 로그) - SystemSettingsPage: 활동 로그 모니터링 기능 개선 🔧 백엔드 수정 (테스트용): - files.py: 리비전 증분 계산 로직 수정 (전체 재분류 → 차이분만 분류) - create_system_admin.py: database_url → get_database_url() 수정 ⚠️ 주의: 이 커밋은 테스트 환경에서의 변경사항입니다.
This commit is contained in:
@@ -380,10 +380,12 @@ async def upload_file(
|
||||
|
||||
# 리비전 업로드인 경우 차이분 계산
|
||||
materials_diff = []
|
||||
original_materials_to_classify = materials_to_classify.copy() # 원본 보존
|
||||
|
||||
if parent_file_id is not None:
|
||||
# 새 파일의 자재들을 수량별로 그룹화
|
||||
new_materials_grouped = {}
|
||||
for material_data in materials_to_classify:
|
||||
for material_data in original_materials_to_classify:
|
||||
description = material_data["original_description"]
|
||||
size_spec = material_data["size_spec"]
|
||||
quantity = float(material_data.get("quantity", 0))
|
||||
@@ -434,6 +436,9 @@ async def upload_file(
|
||||
# 차이분만 처리하도록 materials_to_classify 교체
|
||||
materials_to_classify = materials_diff
|
||||
print(f"차이분 자재 개수: {len(materials_to_classify)}")
|
||||
print(f"🔄 리비전 업로드: 차이분 {len(materials_diff)}개만 분류 처리")
|
||||
else:
|
||||
print(f"🆕 신규 업로드: 전체 {len(materials_to_classify)}개 분류 처리")
|
||||
|
||||
# 분류가 필요한 자재 처리
|
||||
print(f"분류할 자재 총 개수: {len(materials_to_classify)}")
|
||||
@@ -2701,4 +2706,205 @@ async def confirm_material_purchase_api(
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print(f"구매수량 확정 실패: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"구매수량 확정 실패: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"구매수량 확정 실패: {str(e)}")
|
||||
|
||||
|
||||
@router.put("/{file_id}")
|
||||
async def update_file_info(
|
||||
file_id: int,
|
||||
bom_name: Optional[str] = Body(None),
|
||||
description: Optional[str] = Body(None),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""파일 정보 수정"""
|
||||
try:
|
||||
# 파일 존재 확인 및 관련 정보 조회
|
||||
file_query = text("""
|
||||
SELECT id, bom_name, description, job_no, original_filename
|
||||
FROM files
|
||||
WHERE id = :file_id
|
||||
""")
|
||||
file_result = db.execute(file_query, {"file_id": file_id}).fetchone()
|
||||
|
||||
if not file_result:
|
||||
raise HTTPException(status_code=404, detail="파일을 찾을 수 없습니다")
|
||||
|
||||
# 업데이트할 필드 준비
|
||||
update_fields = []
|
||||
params = {}
|
||||
|
||||
if bom_name is not None:
|
||||
update_fields.append("bom_name = :bom_name")
|
||||
params["bom_name"] = bom_name
|
||||
|
||||
if description is not None:
|
||||
update_fields.append("description = :description")
|
||||
params["description"] = description
|
||||
|
||||
if not update_fields:
|
||||
raise HTTPException(status_code=400, detail="수정할 정보가 없습니다")
|
||||
|
||||
# BOM 이름 수정인 경우, 같은 job_no의 모든 리비전을 함께 업데이트
|
||||
if bom_name is not None and file_result.job_no:
|
||||
# 같은 job_no의 모든 파일 업데이트
|
||||
update_query = text(f"""
|
||||
UPDATE files
|
||||
SET {', '.join(update_fields)}, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE job_no = :job_no
|
||||
""")
|
||||
params["job_no"] = file_result.job_no
|
||||
|
||||
else:
|
||||
# 단일 파일만 업데이트
|
||||
update_query = text(f"""
|
||||
UPDATE files
|
||||
SET {', '.join(update_fields)}, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = :file_id
|
||||
""")
|
||||
params["file_id"] = file_id
|
||||
|
||||
db.execute(update_query, params)
|
||||
db.commit()
|
||||
|
||||
# 활동 로그 기록 - 간단하게 처리
|
||||
logger.info(f"파일 정보 수정 완료: 사용자={current_user['username']}, 파일ID={file_id}, BOM명={bom_name or 'N/A'}")
|
||||
|
||||
return {"message": "파일 정보가 성공적으로 수정되었습니다"}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"파일 정보 수정 실패: {str(e)}")
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=500, detail=f"파일 정보 수정 실패: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/{file_id}/export-excel")
|
||||
async def export_materials_to_excel(
|
||||
file_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""자재 목록을 엑셀로 내보내기"""
|
||||
try:
|
||||
# 파일 정보 조회
|
||||
file_query = text("""
|
||||
SELECT f.id, f.original_filename, f.bom_name, f.job_no, f.revision,
|
||||
p.project_name, p.official_project_code
|
||||
FROM files f
|
||||
LEFT JOIN projects p ON f.project_id = p.id
|
||||
WHERE f.id = :file_id
|
||||
""")
|
||||
file_result = db.execute(file_query, {"file_id": file_id}).fetchone()
|
||||
|
||||
if not file_result:
|
||||
raise HTTPException(status_code=404, detail="파일을 찾을 수 없습니다")
|
||||
|
||||
# 자재 목록 조회
|
||||
materials_query = text("""
|
||||
SELECT
|
||||
m.line_number,
|
||||
m.original_description,
|
||||
m.quantity,
|
||||
m.unit,
|
||||
m.size_spec,
|
||||
m.main_nom,
|
||||
m.red_nom,
|
||||
m.material_grade,
|
||||
m.classified_category,
|
||||
m.classification_confidence,
|
||||
m.is_verified,
|
||||
m.verified_by,
|
||||
m.created_at
|
||||
FROM materials m
|
||||
WHERE m.file_id = :file_id
|
||||
ORDER BY m.line_number ASC
|
||||
""")
|
||||
|
||||
materials_result = db.execute(materials_query, {"file_id": file_id}).fetchall()
|
||||
|
||||
# 엑셀 데이터 준비
|
||||
excel_data = []
|
||||
for material in materials_result:
|
||||
excel_data.append({
|
||||
"라인번호": material.line_number,
|
||||
"품명": material.original_description,
|
||||
"수량": material.quantity,
|
||||
"단위": material.unit,
|
||||
"사이즈": material.size_spec,
|
||||
"주요NOM": material.main_nom,
|
||||
"축소NOM": material.red_nom,
|
||||
"재질등급": material.material_grade,
|
||||
"분류": material.classified_category,
|
||||
"신뢰도": material.classification_confidence,
|
||||
"검증여부": "검증완료" if material.is_verified else "미검증",
|
||||
"검증자": material.verified_by or "",
|
||||
"등록일": material.created_at.strftime("%Y-%m-%d %H:%M:%S") if material.created_at else ""
|
||||
})
|
||||
|
||||
# 활동 로그 기록
|
||||
activity_logger = ActivityLogger(db)
|
||||
activity_logger.log_activity(
|
||||
username=current_user["username"],
|
||||
activity_type="엑셀 내보내기",
|
||||
activity_description=f"자재 목록 엑셀 내보내기: {file_result.original_filename}",
|
||||
target_type="file",
|
||||
target_id=file_id,
|
||||
metadata={
|
||||
"file_name": file_result.original_filename,
|
||||
"bom_name": file_result.bom_name,
|
||||
"job_no": file_result.job_no,
|
||||
"revision": file_result.revision,
|
||||
"materials_count": len(excel_data)
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"message": "엑셀 내보내기 준비 완료",
|
||||
"file_info": {
|
||||
"filename": file_result.original_filename,
|
||||
"bom_name": file_result.bom_name,
|
||||
"job_no": file_result.job_no,
|
||||
"revision": file_result.revision,
|
||||
"project_name": file_result.project_name
|
||||
},
|
||||
"materials": excel_data,
|
||||
"total_count": len(excel_data)
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"엑셀 내보내기 실패: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"엑셀 내보내기 실패: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/{file_id}/materials/view-log")
|
||||
async def log_materials_view(
|
||||
file_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""자재 목록 조회 로그 기록"""
|
||||
try:
|
||||
# 파일 정보 조회
|
||||
file_query = text("SELECT original_filename, bom_name FROM files WHERE id = :file_id")
|
||||
file_result = db.execute(file_query, {"file_id": file_id}).fetchone()
|
||||
|
||||
if file_result:
|
||||
# 활동 로그 기록
|
||||
activity_logger = ActivityLogger(db)
|
||||
activity_logger.log_activity(
|
||||
username=current_user["username"],
|
||||
activity_type="자재 목록 조회",
|
||||
activity_description=f"자재 목록 조회: {file_result.original_filename}",
|
||||
target_type="file",
|
||||
target_id=file_id,
|
||||
metadata={
|
||||
"file_name": file_result.original_filename,
|
||||
"bom_name": file_result.bom_name
|
||||
}
|
||||
)
|
||||
|
||||
return {"message": "조회 로그 기록 완료"}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"조회 로그 기록 실패: {str(e)}")
|
||||
return {"message": "조회 로그 기록 실패"}
|
||||
Reference in New Issue
Block a user