284 lines
11 KiB
Python
284 lines
11 KiB
Python
"""
|
|
보고서 처리 관리자 (Report Manager)
|
|
|
|
AppController의 보고서 생성, 인쇄, PDF 변환 로직을 전담합니다.
|
|
사용자의 보고서 생성 요청을 받아 ReportService를 호출하고,
|
|
결과를 UI에 표시하는 중재자 역할을 수행합니다.
|
|
|
|
주요 기능:
|
|
- 보고서 생성 요청 처리
|
|
- 기존 파일 존재 시 사용자 선택 다이얼로그
|
|
- 부분 실패 에러 처리 (열차번호 조회 실패 등)
|
|
- HWP 인쇄 기능
|
|
- PDF 변환 기능
|
|
|
|
아키텍처:
|
|
AppController
|
|
├── ReportManager (보고서 처리)
|
|
│ └── ReportService (실제 생성)
|
|
└── ...
|
|
|
|
작성자: KH.Choi
|
|
최종 수정: 2026-02-17
|
|
버전: 1.0 (Controller에서 분리)
|
|
"""
|
|
import os
|
|
from tkinter import messagebox
|
|
|
|
from core.exceptions import ReportError, is_partial_failure
|
|
|
|
|
|
class ReportManager:
|
|
"""
|
|
보고서 생성 및 처리 관리자
|
|
|
|
보고서 생성, 인쇄, PDF 변환 등의 모든 보고서 관련 작업을 처리합니다.
|
|
에러 처리와 사용자 피드백을 포함하여 안정적인 보고서 생성을 보장합니다.
|
|
|
|
Attributes:
|
|
controller: AppController 인스턴스 (의존성 주입)
|
|
report_service: ReportService 인스턴스
|
|
logger: 로거 인스턴스
|
|
root: Tkinter 루트 윈도우
|
|
settings: 설정 딕셔너리
|
|
|
|
주요 메서드:
|
|
request_create_report: 보고서 생성 요청 진입점
|
|
request_print_voc: 인쇄 요청
|
|
request_create_pdf: PDF 생성 요청
|
|
|
|
사용 예시:
|
|
>>> report_mgr = ReportManager(controller)
|
|
>>> report_mgr.request_create_report(voc_data, parent_window)
|
|
"""
|
|
|
|
def __init__(self, controller):
|
|
"""
|
|
ReportManager 초기화
|
|
|
|
Args:
|
|
controller (AppController): 메인 컨트롤러 인스턴스
|
|
"""
|
|
self.controller = controller
|
|
self.report_service = controller.report_service
|
|
self.logger = controller.logger
|
|
self.root = controller.root
|
|
self.settings = controller.settings
|
|
|
|
# ========================================================================
|
|
# 보고서 생성 (Report Generation)
|
|
# ========================================================================
|
|
|
|
def request_create_report(self, data: dict, parent=None):
|
|
"""
|
|
보고서 생성 요청을 받아 옵션 다이얼로그를 엽니다.
|
|
|
|
역명 정보를 기반으로 기본 사업소를 유추하고,
|
|
사용자가 옵션을 선택하면 실제 생성 로직을 실행합니다.
|
|
|
|
Args:
|
|
data (dict): VOC 데이터
|
|
- id: VOC ID
|
|
- title: 제목
|
|
- content: 내용
|
|
- date: 날짜
|
|
- department: 부서
|
|
- station (optional): 역명
|
|
parent (Widget, optional): 부모 윈도우
|
|
|
|
Raises:
|
|
Exception: 다이얼로그 생성 실패 시
|
|
"""
|
|
try:
|
|
# 역명에 따른 기본 사업소 유추
|
|
station = data.get('station', '')
|
|
if station:
|
|
if any(x in station for x in ["신평", "다대포", "중앙", "부산역"]):
|
|
data['office'] = "신평차량사업소"
|
|
elif any(x in station for x in ["노포", "범어사", "동래", "연산"]):
|
|
data['office'] = "노포차량사업소"
|
|
|
|
# 콜백 함수: 다이얼로그에서 '생성'을 누르면 실행
|
|
def actual_generate(data, options):
|
|
self._generate_report_with_options(data, options, parent)
|
|
|
|
from view.dialogs.report_option_dialog import ReportOptionDialog
|
|
target_parent = parent if parent else self.root
|
|
ReportOptionDialog(target_parent, self.controller, data, callback=actual_generate)
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"보고서 생성 요청 실패: {e}", exc_info=True)
|
|
messagebox.showerror("오류", f"보고서 생성을 시작할 수 없습니다.\n{e}")
|
|
|
|
def _generate_report_with_options(self, data: dict, options: dict, parent):
|
|
"""
|
|
보고서 생성 실행 (내부 메서드)
|
|
|
|
사용자가 선택한 옵션을 데이터에 병합하고,
|
|
ReportService를 호출하여 HWP 보고서를 생성합니다.
|
|
|
|
에러 처리:
|
|
- 부분 실패 (열차번호 조회 실패): 경고만 표시하고 계속
|
|
- 전체 실패 (템플릿 없음 등): 에러 표시 및 중단
|
|
- 기존 파일 존재: 사용자 선택 다이얼로그 표시
|
|
|
|
Args:
|
|
data (dict): VOC 데이터
|
|
options (dict): 보고서 옵션
|
|
- office: 사업소
|
|
- team: 팀
|
|
- reporter_name: 보고자
|
|
- position: 직위
|
|
- action_taken: 조치사항
|
|
parent (Widget): 부모 윈도우
|
|
"""
|
|
try:
|
|
# 사업소별 전화번호 매핑
|
|
tel_map = self.settings.get('master_data', {}).get('office_tel_map', {})
|
|
office = options.get('office', '')
|
|
options['reporter_tel'] = tel_map.get(office, "000-0000")
|
|
|
|
# 옵션을 데이터에 병합
|
|
data.update(options)
|
|
|
|
# ReportService 호출
|
|
result = self.report_service.create_hwp_report(data)
|
|
|
|
# 결과 처리
|
|
if isinstance(result, tuple) and len(result) == 2:
|
|
status, msg_or_path = result
|
|
|
|
if status == "FILE_EXISTS":
|
|
# 기존 파일 존재
|
|
self._handle_existing_report(msg_or_path, data, parent)
|
|
elif status:
|
|
# 정상 생성 완료
|
|
file_path = msg_or_path.split('\n')[1] if '\n' in msg_or_path else msg_or_path
|
|
from view.dialogs.report_complete_dialog import ReportCompleteDialog
|
|
self.root.after(1000, lambda: ReportCompleteDialog(parent, file_path))
|
|
else:
|
|
# 생성 실패
|
|
messagebox.showerror("보고서 생성 실패", msg_or_path)
|
|
else:
|
|
messagebox.showerror("오류", "예상치 못한 응답 형식입니다.")
|
|
|
|
except ReportError as e:
|
|
if is_partial_failure(e):
|
|
# 부분 실패: 경고만 표시하고 계속
|
|
self.logger.warning(f"부분 실패: {e.message}")
|
|
messagebox.showwarning("경고", e.message)
|
|
else:
|
|
# 전체 실패
|
|
self.logger.error(f"보고서 생성 실패: {e.message}")
|
|
messagebox.showerror("오류", e.message)
|
|
except Exception as e:
|
|
self.logger.error(f"보고서 생성 중 오류: {e}", exc_info=True)
|
|
messagebox.showerror("오류", f"보고서 생성 중 오류가 발생했습니다.\n{e}")
|
|
|
|
def _handle_existing_report(self, file_path: str, data: dict, parent):
|
|
"""
|
|
기존 보고서 존재 시 사용자 선택 처리 (내부 메서드)
|
|
|
|
사용자에게 3가지 선택지를 제공합니다:
|
|
- 예: 기존 파일 열기
|
|
- 아니오: 새로 생성 (기존 파일 덮어쓰기)
|
|
- 취소: 작업 취소
|
|
|
|
Args:
|
|
file_path (str): 기존 파일 경로
|
|
data (dict): VOC 데이터
|
|
parent (Widget): 부모 윈도우
|
|
"""
|
|
from tkinter import messagebox as mb
|
|
|
|
choice = mb.askyesnocancel(
|
|
"기존 보고서 발견",
|
|
f"동일한 보고서가 이미 존재합니다:\n\n{file_path}\n\n"
|
|
"• 예: 기존 파일 열기\n"
|
|
"• 아니오: 새로 생성 (기존 파일 덮어쓰기)\n"
|
|
"• 취소: 작업 취소"
|
|
)
|
|
|
|
if choice is True:
|
|
# 기존 파일 열기
|
|
os.startfile(file_path)
|
|
from view.dialogs.report_complete_dialog import ReportCompleteDialog
|
|
self.root.after(500, lambda: ReportCompleteDialog(parent, file_path))
|
|
elif choice is False:
|
|
# 새로 생성 (강제 덮어쓰기)
|
|
try:
|
|
os.remove(file_path)
|
|
success2, msg2 = self.report_service.create_hwp_report(data)
|
|
if success2:
|
|
file_path2 = msg2.split('\n')[1] if '\n' in msg2 else msg2
|
|
from view.dialogs.report_complete_dialog import ReportCompleteDialog
|
|
self.root.after(1000, lambda: ReportCompleteDialog(parent, file_path2))
|
|
else:
|
|
messagebox.showerror("보고서 생성 실패", msg2)
|
|
except Exception as e:
|
|
messagebox.showerror("오류", f"파일 삭제 실패: {e}")
|
|
# choice is None (취소): 아무것도 하지 않음
|
|
|
|
# ========================================================================
|
|
# 인쇄 및 PDF (Print & PDF)
|
|
# ========================================================================
|
|
|
|
def request_print_voc(self, data: dict):
|
|
"""
|
|
인쇄 요청을 받아 ReportService 호출
|
|
|
|
Windows 기본 프린터로 VOC 상세 내용을 출력합니다.
|
|
|
|
Args:
|
|
data (dict): 인쇄할 VOC 데이터
|
|
- id: VOC ID
|
|
- title: 제목
|
|
- content: 내용
|
|
- answer: 답변 (optional)
|
|
|
|
Raises:
|
|
ReportError: 인쇄 실패 시
|
|
"""
|
|
try:
|
|
success, msg = self.report_service.print_voc_detail(data)
|
|
if success:
|
|
messagebox.showinfo("인쇄 성공", msg)
|
|
else:
|
|
messagebox.showerror("인쇄 실패", msg)
|
|
except ReportError as e:
|
|
self.logger.error(f"인쇄 실패: {e.message}")
|
|
messagebox.showerror("오류", e.message)
|
|
except Exception as e:
|
|
self.logger.error(f"인쇄 중 오류: {e}", exc_info=True)
|
|
messagebox.showerror("오류", f"인쇄 중 오류가 발생했습니다.\n{e}")
|
|
|
|
def request_create_pdf(self, data: dict):
|
|
"""
|
|
PDF 생성 요청을 받아 ReportService 호출
|
|
|
|
VOC 데이터를 PDF 형식으로 변환하여 저장합니다.
|
|
|
|
Args:
|
|
data (dict): VOC 데이터
|
|
- id: VOC ID
|
|
- title: 제목
|
|
- content: 내용
|
|
- date: 날짜
|
|
- department: 부서
|
|
|
|
Raises:
|
|
ReportError: PDF 생성 실패 시
|
|
"""
|
|
try:
|
|
success, msg = self.report_service.create_pdf_report(data)
|
|
if success:
|
|
messagebox.showinfo("PDF 생성 성공", msg)
|
|
else:
|
|
messagebox.showerror("PDF 생성 실패", msg)
|
|
except ReportError as e:
|
|
self.logger.error(f"PDF 생성 실패: {e.message}")
|
|
messagebox.showerror("오류", e.message)
|
|
except Exception as e:
|
|
self.logger.error(f"PDF 생성 중 오류: {e}", exc_info=True)
|
|
messagebox.showerror("오류", f"PDF 생성 중 오류가 발생했습니다.\n{e}")
|