// controllers/dailyWorkReportController.js - 권한별 전체 조회 지원 버전 const dailyWorkReportModel = require('../models/dailyWorkReportModel'); const dailyWorkReportService = require('../services/dailyWorkReportService'); /** * 📝 작업보고서 생성 (V2 - Service Layer 사용) */ const createDailyWorkReport = async (req, res) => { try { const reportData = { ...req.body, created_by: req.user?.user_id || req.user?.id, created_by_name: req.user?.name || req.user?.username || '알 수 없는 사용자' }; const result = await dailyWorkReportService.createDailyWorkReportService(reportData); res.status(201).json({ success: true, timestamp: new Date().toISOString(), ...result }); } catch (error) { console.error('💥 작업보고서 생성 컨트롤러 오류:', error.message); res.status(400).json({ success: false, error: '작업보고서 생성에 실패했습니다.', details: error.message }); } }; /** * �� 누적 현황 조회 (새로운 기능) */ const getAccumulatedReports = (req, res) => { const { date, worker_id } = req.query; if (!date || !worker_id) { return res.status(400).json({ error: 'date와 worker_id가 필요합니다.', example: 'date=2024-06-16&worker_id=1' }); } console.log(`📊 누적 현황 조회: date=${date}, worker_id=${worker_id}`); dailyWorkReportModel.getAccumulatedReportsByDate(date, worker_id, (err, data) => { if (err) { console.error('누적 현황 조회 오류:', err); return res.status(500).json({ error: '누적 현황 조회 중 오류가 발생했습니다.', details: err.message }); } console.log(`📊 누적 현황 조회 결과: ${data.length}개`); res.json({ date, worker_id, total_entries: data.length, accumulated_data: data, timestamp: new Date().toISOString() }); }); }; /** * 📊 기여자별 요약 조회 (새로운 기능) */ const getContributorsSummary = (req, res) => { const { date, worker_id } = req.query; if (!date || !worker_id) { return res.status(400).json({ error: 'date와 worker_id가 필요합니다.', example: 'date=2024-06-16&worker_id=1' }); } console.log(`📊 기여자별 요약 조회: date=${date}, worker_id=${worker_id}`); dailyWorkReportModel.getContributorsByDate(date, worker_id, (err, data) => { if (err) { console.error('기여자별 요약 조회 오류:', err); return res.status(500).json({ error: '기여자별 요약 조회 중 오류가 발생했습니다.', details: err.message }); } const totalHours = data.reduce((sum, contributor) => sum + parseFloat(contributor.total_hours || 0), 0); console.log(`📊 기여자별 요약: ${data.length}명, 총 ${totalHours}시간`); res.json({ date, worker_id, contributors: data, total_contributors: data.length, grand_total_hours: totalHours, timestamp: new Date().toISOString() }); }); }; /** * 📊 개인 누적 현황 조회 (새로운 기능) */ const getMyAccumulatedData = (req, res) => { const { date, worker_id } = req.query; const created_by = req.user?.user_id || req.user?.id; if (!date || !worker_id) { return res.status(400).json({ error: 'date와 worker_id가 필요합니다.', example: 'date=2024-06-16&worker_id=1' }); } if (!created_by) { return res.status(401).json({ error: '사용자 인증 정보가 없습니다.' }); } console.log(`📊 개인 누적 현황 조회: date=${date}, worker_id=${worker_id}, created_by=${created_by}`); dailyWorkReportModel.getMyAccumulatedHours(date, worker_id, created_by, (err, data) => { if (err) { console.error('개인 누적 현황 조회 오류:', err); return res.status(500).json({ error: '개인 누적 현황 조회 중 오류가 발생했습니다.', details: err.message }); } console.log(`📊 개인 누적: ${data.my_entry_count}개 항목, ${data.my_total_hours}시간`); res.json({ date, worker_id, created_by, my_data: data, timestamp: new Date().toISOString() }); }); }; /** * 🗑️ 개별 항목 삭제 (본인 작성분만 - 새로운 기능) */ const removeMyEntry = (req, res) => { const { id } = req.params; const deleted_by = req.user?.user_id || req.user?.id; if (!deleted_by) { return res.status(401).json({ error: '사용자 인증 정보가 없습니다.' }); } console.log(`🗑️ 개별 항목 삭제 요청: id=${id}, 삭제자=${deleted_by}`); dailyWorkReportModel.removeSpecificEntry(id, deleted_by, (err, result) => { if (err) { console.error('개별 항목 삭제 오류:', err); return res.status(500).json({ error: '항목 삭제 중 오류가 발생했습니다.', details: err.message }); } console.log(`✅ 개별 항목 삭제 완료: id=${id}`); res.json({ message: '항목이 성공적으로 삭제되었습니다.', id: id, deleted_by, timestamp: new Date().toISOString(), ...result }); }); }; /** * 📊 작업보고서 조회 (권한별 전체 조회 지원 - 핵심 수정!) */ const getDailyWorkReports = (req, res) => { const { date, worker_id, created_by: requested_created_by, view_all, admin, all, no_filter, ignore_created_by } = req.query; const current_user_id = req.user?.user_id || req.user?.id; const user_access_level = req.user?.access_level; if (!current_user_id) { return res.status(401).json({ error: '사용자 인증 정보가 없습니다.' }); } // 🎯 권한별 필터링 로직 개선 const isAdmin = user_access_level === 'system' || user_access_level === 'admin'; const hasViewAllFlag = view_all === 'true' || admin === 'true' || all === 'true' || no_filter === 'true' || ignore_created_by === 'true' || requested_created_by === 'all' || requested_created_by === ''; const canViewAll = isAdmin || hasViewAllFlag; // 관리자가 아니고 전체 조회 플래그도 없으면 본인 작성분으로 제한 let final_created_by = null; if (!canViewAll) { final_created_by = requested_created_by || current_user_id; } else if (requested_created_by && requested_created_by !== 'all' && requested_created_by !== '') { final_created_by = requested_created_by; } console.log('📊 작업보고서 조회 요청:', { date, worker_id, requested_created_by, current_user_id, user_access_level, isAdmin, hasViewAllFlag, canViewAll, final_created_by }); if (date && final_created_by) { // 날짜 + 작성자별 조회 dailyWorkReportModel.getByDateAndCreator(date, final_created_by, (err, data) => { if (err) { console.error('작업보고서 조회 오류:', err); return res.status(500).json({ error: '작업보고서 조회 중 오류가 발생했습니다.', details: err.message }); } console.log(`📊 날짜+작성자별 조회 결과: ${data.length}개`); res.json(data); }); } else if (date && worker_id) { // 날짜 + 작업자별 조회 dailyWorkReportModel.getByDateAndWorker(date, worker_id, (err, data) => { if (err) { console.error('작업보고서 조회 오류:', err); return res.status(500).json({ error: '작업보고서 조회 중 오류가 발생했습니다.', details: err.message }); } // 🎯 권한별 필터링 let finalData = data; if (!canViewAll) { finalData = data.filter(report => report.created_by === current_user_id); console.log(`📊 권한 필터링: 전체 ${data.length}개 → ${finalData.length}개`); } else { console.log(`📊 관리자/전체 조회 권한: ${data.length}개 전체 반환`); } res.json(finalData); }); } else if (date) { // 날짜별 조회 dailyWorkReportModel.getByDate(date, (err, data) => { if (err) { console.error('작업보고서 조회 오류:', err); return res.status(500).json({ error: '작업보고서 조회 중 오류가 발생했습니다.', details: err.message }); } // 🎯 권한별 필터링 let finalData = data; if (!canViewAll) { finalData = data.filter(report => report.created_by === current_user_id); console.log(`📊 권한 필터링: 전체 ${data.length}개 → ${finalData.length}개`); } else { console.log(`📊 관리자/전체 조회 권한: ${data.length}개 전체 반환`); } res.json(finalData); }); } else { res.status(400).json({ error: '날짜(date) 파라미터가 필요합니다.', example: 'date=2024-06-16', optional: ['worker_id', 'created_by', 'view_all', 'admin', 'all'] }); } }; /** * 📊 날짜별 작업보고서 조회 (경로 파라미터 - 권한별 전체 조회 지원) */ const getDailyWorkReportsByDate = (req, res) => { const { date } = req.params; const current_user_id = req.user?.user_id || req.user?.id; const user_access_level = req.user?.access_level; if (!current_user_id) { return res.status(401).json({ error: '사용자 인증 정보가 없습니다.' }); } const isAdmin = user_access_level === 'system' || user_access_level === 'admin'; console.log(`📊 날짜별 조회 (경로): date=${date}, user=${current_user_id}, 권한=${user_access_level}, 관리자=${isAdmin}`); dailyWorkReportModel.getByDate(date, (err, data) => { if (err) { console.error('날짜별 작업보고서 조회 오류:', err); return res.status(500).json({ error: '작업보고서 조회 중 오류가 발생했습니다.', details: err.message }); } // 🎯 권한별 필터링 let finalData = data; if (!isAdmin) { finalData = data.filter(report => report.created_by === current_user_id); console.log(`📊 권한 필터링: 전체 ${data.length}개 → ${finalData.length}개`); } else { console.log(`📊 관리자 권한으로 전체 조회: ${data.length}개`); } res.json(finalData); }); }; /** * 🔍 작업보고서 검색 (페이지네이션 포함) */ const searchWorkReports = (req, res) => { const { start_date, end_date, worker_id, project_id, work_status_id, page = 1, limit = 20 } = req.query; const created_by = req.user?.user_id || req.user?.id; if (!start_date || !end_date) { return res.status(400).json({ error: 'start_date와 end_date가 필요합니다.', example: 'start_date=2024-01-01&end_date=2024-01-31', optional: ['worker_id', 'project_id', 'work_status_id', 'page', 'limit'] }); } if (!created_by) { return res.status(401).json({ error: '사용자 인증 정보가 없습니다.' }); } const searchParams = { start_date, end_date, worker_id: worker_id ? parseInt(worker_id) : null, project_id: project_id ? parseInt(project_id) : null, work_status_id: work_status_id ? parseInt(work_status_id) : null, created_by, // 작성자 필터링 추가 page: parseInt(page), limit: parseInt(limit) }; console.log('🔍 작업보고서 검색 요청:', searchParams); dailyWorkReportModel.searchWithDetails(searchParams, (err, data) => { if (err) { console.error('작업보고서 검색 오류:', err); return res.status(500).json({ error: '작업보고서 검색 중 오류가 발생했습니다.', details: err.message }); } console.log(`🔍 검색 결과: ${data.reports?.length || 0}개 (전체: ${data.total || 0}개)`); res.json(data); }); }; /** * 📈 통계 조회 (작성자별 필터링) */ const getWorkReportStats = (req, res) => { const { start_date, end_date } = req.query; const created_by = req.user?.user_id || req.user?.id; if (!start_date || !end_date) { return res.status(400).json({ error: 'start_date와 end_date가 필요합니다.', example: 'start_date=2024-01-01&end_date=2024-01-31' }); } if (!created_by) { return res.status(401).json({ error: '사용자 인증 정보가 없습니다.' }); } console.log(`📈 통계 조회: ${start_date} ~ ${end_date}, 요청자: ${created_by}`); dailyWorkReportModel.getStatistics(start_date, end_date, (err, data) => { if (err) { console.error('통계 조회 오류:', err); return res.status(500).json({ error: '통계 조회 중 오류가 발생했습니다.', details: err.message }); } res.json({ ...data, metadata: { note: '현재는 전체 통계입니다. 개인별 통계는 추후 구현 예정', requested_by: created_by, period: `${start_date} ~ ${end_date}`, timestamp: new Date().toISOString() } }); }); }; /** * 📊 일일 근무 요약 조회 */ const getDailySummary = (req, res) => { const { date, worker_id } = req.query; if (date) { console.log(`📊 일일 요약 조회: date=${date}`); dailyWorkReportModel.getSummaryByDate(date, (err, data) => { if (err) { console.error('일일 요약 조회 오류:', err); return res.status(500).json({ error: '일일 요약 조회 중 오류가 발생했습니다.', details: err.message }); } res.json(data); }); } else if (worker_id) { console.log(`📊 작업자별 요약 조회: worker_id=${worker_id}`); dailyWorkReportModel.getSummaryByWorker(worker_id, (err, data) => { if (err) { console.error('작업자별 요약 조회 오류:', err); return res.status(500).json({ error: '작업자별 요약 조회 중 오류가 발생했습니다.', details: err.message }); } res.json(data); }); } else { res.status(400).json({ error: 'date 또는 worker_id 파라미터가 필요합니다.', examples: [ 'date=2024-06-16', 'worker_id=1' ] }); } }; /** * 📅 월간 요약 조회 */ const getMonthlySummary = (req, res) => { const { year, month } = req.query; if (!year || !month) { return res.status(400).json({ error: 'year와 month가 필요합니다.', example: 'year=2024&month=01', note: 'month는 01, 02, ..., 12 형식으로 입력하세요.' }); } console.log(`📅 월간 요약 조회: ${year}-${month}`); dailyWorkReportModel.getMonthlySummary(year, month, (err, data) => { if (err) { console.error('월간 요약 조회 오류:', err); return res.status(500).json({ error: '월간 요약 조회 중 오류가 발생했습니다.', details: err.message }); } res.json({ year: parseInt(year), month: parseInt(month), summary: data, total_entries: data.length, timestamp: new Date().toISOString() }); }); }; /** * ✏️ 작업보고서 수정 */ const updateWorkReport = (req, res) => { const { id } = req.params; const updateData = req.body; const updated_by = req.user?.user_id || req.user?.id; if (!updated_by) { return res.status(401).json({ error: '사용자 인증 정보가 없습니다.' }); } updateData.updated_by = updated_by; console.log(`✏️ 작업보고서 수정 요청: id=${id}, 수정자=${updated_by}`); dailyWorkReportModel.updateById(id, updateData, (err, affectedRows) => { if (err) { console.error('작업보고서 수정 오류:', err); return res.status(500).json({ error: '작업보고서 수정 중 오류가 발생했습니다.', details: err.message }); } if (affectedRows === 0) { return res.status(404).json({ error: '수정할 작업보고서를 찾을 수 없습니다.', id: id }); } console.log(`✅ 작업보고서 수정 완료: id=${id}`); res.json({ message: '작업보고서가 성공적으로 수정되었습니다.', id: id, affected_rows: affectedRows, updated_by, timestamp: new Date().toISOString() }); }); }; /** * 🗑️ 특정 작업보고서 삭제 */ const removeDailyWorkReport = (req, res) => { const { id } = req.params; const deleted_by = req.user?.user_id || req.user?.id; if (!deleted_by) { return res.status(401).json({ error: '사용자 인증 정보가 없습니다.' }); } console.log(`🗑️ 작업보고서 삭제 요청: id=${id}, 삭제자=${deleted_by}`); dailyWorkReportModel.removeById(id, deleted_by, (err, affectedRows) => { if (err) { console.error('작업보고서 삭제 오류:', err); return res.status(500).json({ error: '작업보고서 삭제 중 오류가 발생했습니다.', details: err.message }); } if (affectedRows === 0) { return res.status(404).json({ error: '삭제할 작업보고서를 찾을 수 없습니다.', id: id }); } console.log(`✅ 작업보고서 삭제 완료: id=${id}`); res.json({ message: '작업보고서가 성공적으로 삭제되었습니다.', id: id, affected_rows: affectedRows, deleted_by, timestamp: new Date().toISOString() }); }); }; /** * 🗑️ 작업자의 특정 날짜 전체 삭제 */ const removeDailyWorkReportByDateAndWorker = (req, res) => { const { date, worker_id } = req.params; const deleted_by = req.user?.user_id || req.user?.id; if (!deleted_by) { return res.status(401).json({ error: '사용자 인증 정보가 없습니다.' }); } console.log(`🗑️ 날짜+작업자별 전체 삭제 요청: date=${date}, worker_id=${worker_id}, 삭제자=${deleted_by}`); dailyWorkReportModel.removeByDateAndWorker(date, worker_id, deleted_by, (err, affectedRows) => { if (err) { console.error('작업보고서 전체 삭제 오류:', err); return res.status(500).json({ error: '작업보고서 삭제 중 오류가 발생했습니다.', details: err.message }); } if (affectedRows === 0) { return res.status(404).json({ error: '삭제할 작업보고서를 찾을 수 없습니다.', date: date, worker_id: worker_id }); } console.log(`✅ 날짜+작업자별 전체 삭제 완료: ${affectedRows}개`); res.json({ message: `${date} 날짜의 작업자 ${worker_id} 작업보고서 ${affectedRows}개가 삭제되었습니다.`, date, worker_id, affected_rows: affectedRows, deleted_by, timestamp: new Date().toISOString() }); }); }; /** * 📋 마스터 데이터 조회 함수들 */ const getWorkTypes = (req, res) => { console.log('📋 작업 유형 조회 요청'); dailyWorkReportModel.getAllWorkTypes((err, data) => { if (err) { console.error('작업 유형 조회 오류:', err); return res.status(500).json({ error: '작업 유형 조회 중 오류가 발생했습니다.', details: err.message }); } console.log(`📋 작업 유형 조회 결과: ${data.length}개`); res.json(data); }); }; const getWorkStatusTypes = (req, res) => { console.log('📋 업무 상태 유형 조회 요청'); dailyWorkReportModel.getAllWorkStatusTypes((err, data) => { if (err) { console.error('업무 상태 유형 조회 오류:', err); return res.status(500).json({ error: '업무 상태 유형 조회 중 오류가 발생했습니다.', details: err.message }); } console.log(`📋 업무 상태 유형 조회 결과: ${data.length}개`); res.json(data); }); }; const getErrorTypes = (req, res) => { console.log('📋 에러 유형 조회 요청'); dailyWorkReportModel.getAllErrorTypes((err, data) => { if (err) { console.error('에러 유형 조회 오류:', err); return res.status(500).json({ error: '에러 유형 조회 중 오류가 발생했습니다.', details: err.message }); } console.log(`📋 에러 유형 조회 결과: ${data.length}개`); res.json(data); }); }; // 모든 컨트롤러 함수 내보내기 (권한별 조회 지원) module.exports = { // 📝 핵심 CRUD 함수들 (권한별 전체 조회 지원) createDailyWorkReport, // 누적 추가 (덮어쓰기 없음) getDailyWorkReports, // 조회 (권한별 필터링 개선) getDailyWorkReportsByDate, // 날짜별 조회 (권한별 필터링 개선) searchWorkReports, // 검색 (페이지네이션) updateWorkReport, // 수정 removeDailyWorkReport, // 개별 삭제 removeDailyWorkReportByDateAndWorker, // 전체 삭제 // 🔄 누적 관련 새로운 함수들 getAccumulatedReports, // 누적 현황 조회 getContributorsSummary, // 기여자별 요약 getMyAccumulatedData, // 개인 누적 현황 removeMyEntry, // 개별 항목 삭제 (본인 것만) // 📊 요약 및 통계 함수들 getDailySummary, // 일일 요약 getMonthlySummary, // 월간 요약 getWorkReportStats, // 통계 // 📋 마스터 데이터 함수들 getWorkTypes, // 작업 유형 목록 getWorkStatusTypes, // 업무 상태 유형 목록 getErrorTypes // 에러 유형 목록 };