refactor: 완료 반려 필드 분리 및 데이터 구조 개선
- backend: completion_rejection_reason 등 전용 필드 추가 - 기존 management_comment에 섞여있던 완료 반려 내용 분리 - 현황판: 완료 반려 내역 별도 카드로 표시 - 관리함: 해결방안에 완료 반려 내용 제외하여 표시 - DB 마이그레이션: completion_rejected_at, completion_rejected_by_id, completion_rejection_reason 필드 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -186,21 +186,66 @@ async def delete_issue(
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
from database.models import DeletionLog
|
||||
import json
|
||||
|
||||
issue = db.query(Issue).filter(Issue.id == issue_id).first()
|
||||
if not issue:
|
||||
raise HTTPException(status_code=404, detail="Issue not found")
|
||||
|
||||
# 권한 확인 (관리자만 삭제 가능)
|
||||
if current_user.role != UserRole.admin:
|
||||
raise HTTPException(status_code=403, detail="Only admin can delete issues")
|
||||
|
||||
|
||||
# 권한 확인 (관리자 또는 본인이 등록한 경우 삭제 가능)
|
||||
if current_user.role != UserRole.admin and issue.reporter_id != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="본인이 등록한 부적합만 삭제할 수 있습니다.")
|
||||
|
||||
# 이 이슈를 중복 대상으로 참조하는 다른 이슈들의 참조 제거
|
||||
referencing_issues = db.query(Issue).filter(Issue.duplicate_of_issue_id == issue_id).all()
|
||||
if referencing_issues:
|
||||
print(f"DEBUG: {len(referencing_issues)}개의 이슈가 이 이슈를 중복 대상으로 참조하고 있습니다. 참조를 제거합니다.")
|
||||
for ref_issue in referencing_issues:
|
||||
ref_issue.duplicate_of_issue_id = None
|
||||
db.flush() # 참조 제거를 먼저 커밋
|
||||
|
||||
# 삭제 로그 생성 (삭제 전 데이터 저장)
|
||||
issue_data = {
|
||||
"id": issue.id,
|
||||
"category": issue.category.value if issue.category else None,
|
||||
"description": issue.description,
|
||||
"status": issue.status.value if issue.status else None,
|
||||
"reporter_id": issue.reporter_id,
|
||||
"project_id": issue.project_id,
|
||||
"report_date": issue.report_date.isoformat() if issue.report_date else None,
|
||||
"work_hours": issue.work_hours,
|
||||
"detail_notes": issue.detail_notes,
|
||||
"photo_path": issue.photo_path,
|
||||
"photo_path2": issue.photo_path2,
|
||||
"review_status": issue.review_status.value if issue.review_status else None,
|
||||
"solution": issue.solution,
|
||||
"responsible_department": issue.responsible_department.value if issue.responsible_department else None,
|
||||
"responsible_person": issue.responsible_person,
|
||||
"expected_completion_date": issue.expected_completion_date.isoformat() if issue.expected_completion_date else None,
|
||||
"actual_completion_date": issue.actual_completion_date.isoformat() if issue.actual_completion_date else None,
|
||||
}
|
||||
|
||||
deletion_log = DeletionLog(
|
||||
entity_type="issue",
|
||||
entity_id=issue.id,
|
||||
entity_data=issue_data,
|
||||
deleted_by_id=current_user.id,
|
||||
reason=f"사용자 {current_user.username}에 의해 삭제됨"
|
||||
)
|
||||
db.add(deletion_log)
|
||||
|
||||
# 이미지 파일 삭제
|
||||
if issue.photo_path:
|
||||
delete_file(issue.photo_path)
|
||||
|
||||
if issue.photo_path2:
|
||||
delete_file(issue.photo_path2)
|
||||
if issue.completion_photo_path:
|
||||
delete_file(issue.completion_photo_path)
|
||||
|
||||
db.delete(issue)
|
||||
db.commit()
|
||||
return {"detail": "Issue deleted successfully"}
|
||||
return {"detail": "Issue deleted successfully", "logged": True}
|
||||
|
||||
@router.get("/stats/summary")
|
||||
async def get_issue_stats(
|
||||
@@ -353,3 +398,66 @@ async def request_completion(
|
||||
except:
|
||||
pass
|
||||
raise HTTPException(status_code=500, detail=f"완료 신청 처리 중 오류가 발생했습니다: {str(e)}")
|
||||
|
||||
@router.post("/{issue_id}/reject-completion")
|
||||
async def reject_completion_request(
|
||||
issue_id: int,
|
||||
request: schemas.CompletionRejectionRequest,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
완료 신청 반려 - 관리자가 완료 신청을 반려
|
||||
"""
|
||||
# 이슈 조회
|
||||
issue = db.query(Issue).filter(Issue.id == issue_id).first()
|
||||
if not issue:
|
||||
raise HTTPException(status_code=404, detail="부적합을 찾을 수 없습니다.")
|
||||
|
||||
# 완료 신청이 있는지 확인
|
||||
if not issue.completion_requested_at:
|
||||
raise HTTPException(status_code=400, detail="완료 신청이 없는 부적합입니다.")
|
||||
|
||||
# 권한 확인 (관리자 또는 관리함 접근 권한이 있는 사용자)
|
||||
if current_user.role != UserRole.admin and not check_page_access(current_user.id, 'issues_management', db):
|
||||
raise HTTPException(status_code=403, detail="완료 반려 권한이 없습니다.")
|
||||
|
||||
try:
|
||||
print(f"DEBUG: 완료 반려 시작 - Issue ID: {issue_id}, User: {current_user.username}")
|
||||
|
||||
# 완료 사진 파일 삭제
|
||||
if issue.completion_photo_path:
|
||||
try:
|
||||
delete_file(issue.completion_photo_path)
|
||||
print(f"DEBUG: 완료 사진 삭제 완료")
|
||||
except Exception as e:
|
||||
print(f"WARNING: 완료 사진 삭제 실패 - {str(e)}")
|
||||
|
||||
# 완료 신청 정보 초기화
|
||||
issue.completion_requested_at = None
|
||||
issue.completion_requested_by_id = None
|
||||
issue.completion_photo_path = None
|
||||
issue.completion_comment = None
|
||||
|
||||
# 완료 반려 정보 기록 (전용 필드 사용)
|
||||
issue.completion_rejected_at = datetime.now()
|
||||
issue.completion_rejected_by_id = current_user.id
|
||||
issue.completion_rejection_reason = request.rejection_reason
|
||||
|
||||
# 상태는 in_progress로 유지
|
||||
issue.review_status = ReviewStatus.in_progress
|
||||
|
||||
db.commit()
|
||||
db.refresh(issue)
|
||||
print(f"DEBUG: 완료 반려 처리 완료")
|
||||
|
||||
return {
|
||||
"message": "완료 신청이 반려되었습니다.",
|
||||
"issue_id": issue.id,
|
||||
"rejection_reason": request.rejection_reason
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR: 완료 반려 처리 오류 - {str(e)}")
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=500, detail=f"완료 반려 처리 중 오류가 발생했습니다: {str(e)}")
|
||||
|
||||
Reference in New Issue
Block a user