Files
tk-factory-services/system1-factory/api/controllers/tbmController.js
Hyungi Ahn 7637be33f3 feat: TBM 모바일 시스템 + 작업 분할/이동 + 권한 통합
TBM 시스템:
- 4단계 워크플로우 (draft→세부편집→완료→작업보고)
- 모바일 전용 TBM 페이지 (tbm-mobile.html) + 3단계 생성 위자드
- 작업자 작업 분할 (work_hours + split_seq)
- 작업자 이동 보내기/빼오기 (tbm_transfers 테이블)
- 생성 시 중복 배정 방지 (당일 배정 현황 조회)
- 데스크탑 TBM 페이지 세부편집 기능 추가

작업보고서:
- 모바일 전용 작업보고서 페이지 (report-create-mobile.html)
- TBM에서 사전 등록된 work_hours 자동 반영

권한 시스템:
- tkuser user_page_permissions 테이블과 system1 페이지 접근 연동
- pageAccessRoutes를 userRoutes보다 먼저 등록 (라우트 우선순위 수정)
- TKUSER_DEFAULT_ACCESS 폴백 추가 (개인→부서→기본값 3단계)
- 권한 캐시키 갱신 (userPageAccess_v2)

기타:
- app-init.js 캐시 버스팅 (v=5)
- iOS Safari touch-action: manipulation 적용
- KST 타임존 날짜 버그 수정 (toISOString UTC 이슈)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 07:46:21 +09:00

1100 lines
30 KiB
JavaScript

// controllers/tbmController.js - TBM 시스템 컨트롤러
const TbmModel = require('../models/tbmModel');
const TbmTransferModel = require('../models/tbmTransferModel');
const TbmController = {
// ==================== TBM 세션 관련 ====================
/**
* TBM 세션 생성
*/
createSession: (req, res) => {
const sessionData = {
session_date: req.body.session_date,
leader_id: req.body.leader_id || null,
project_id: req.body.project_id || null,
work_location: req.body.work_location || null,
work_description: req.body.work_description || null,
safety_notes: req.body.safety_notes || null,
start_time: req.body.start_time || null,
created_by: req.user.user_id
};
// 필수 필드 검증 (날짜만 필수, leader_id는 관리자의 경우 null 허용)
if (!sessionData.session_date) {
return res.status(400).json({
success: false,
message: 'TBM 날짜는 필수입니다.'
});
}
TbmModel.createSession(sessionData, (err, result) => {
if (err) {
console.error('TBM 세션 생성 오류:', err);
return res.status(500).json({
success: false,
message: 'TBM 세션 생성 중 오류가 발생했습니다.',
error: err.message
});
}
res.status(201).json({
success: true,
message: 'TBM 세션이 생성되었습니다.',
data: {
session_id: result.insertId,
...sessionData
}
});
});
},
/**
* 특정 날짜의 TBM 세션 목록 조회
*/
getSessionsByDate: (req, res) => {
const { date } = req.params;
if (!date) {
return res.status(400).json({
success: false,
message: '날짜 정보가 필요합니다.'
});
}
TbmModel.getSessionsByDate(date, (err, results) => {
if (err) {
console.error('TBM 세션 조회 오류:', err);
return res.status(500).json({
success: false,
message: 'TBM 세션 조회 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({
success: true,
data: results
});
});
},
/**
* TBM 세션 상세 조회
*/
getSessionById: (req, res) => {
const { sessionId } = req.params;
TbmModel.getSessionById(sessionId, (err, results) => {
if (err) {
console.error('TBM 세션 상세 조회 오류:', err);
return res.status(500).json({
success: false,
message: 'TBM 세션 상세 조회 중 오류가 발생했습니다.',
error: err.message
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: 'TBM 세션을 찾을 수 없습니다.'
});
}
res.json({
success: true,
data: results[0]
});
});
},
/**
* TBM 세션 수정
*/
updateSession: (req, res) => {
const { sessionId } = req.params;
const sessionData = {
project_id: req.body.project_id,
work_location: req.body.work_location,
work_description: req.body.work_description,
safety_notes: req.body.safety_notes,
status: req.body.status || 'draft'
};
TbmModel.updateSession(sessionId, sessionData, (err, result) => {
if (err) {
console.error('TBM 세션 수정 오류:', err);
return res.status(500).json({
success: false,
message: 'TBM 세션 수정 중 오류가 발생했습니다.',
error: err.message
});
}
if (result.affectedRows === 0) {
return res.status(404).json({
success: false,
message: 'TBM 세션을 찾을 수 없습니다.'
});
}
res.json({
success: true,
message: 'TBM 세션이 수정되었습니다.'
});
});
},
/**
* TBM 세션 완료 처리
*/
completeSession: (req, res) => {
const { sessionId } = req.params;
const endTime = req.body.end_time || new Date().toTimeString().slice(0, 8);
const attendanceData = req.body.attendance_data;
// 근태 데이터가 있으면 새 메서드 사용
if (attendanceData && Array.isArray(attendanceData) && attendanceData.length > 0) {
const createdBy = req.user.user_id;
TbmModel.completeSessionWithAttendance(sessionId, endTime, attendanceData, createdBy, (err, result) => {
if (err) {
console.error('TBM 세션 완료 처리 오류:', err);
return res.status(500).json({
success: false,
message: 'TBM 세션 완료 처리 중 오류가 발생했습니다.',
error: err.message
});
}
if (result.affectedRows === 0) {
return res.status(404).json({
success: false,
message: 'TBM 세션을 찾을 수 없습니다.'
});
}
res.json({
success: true,
message: 'TBM 세션이 완료되었습니다.'
});
});
return;
}
// 기존 방식 (하위 호환)
TbmModel.completeSession(sessionId, endTime, (err, result) => {
if (err) {
console.error('TBM 세션 완료 처리 오류:', err);
return res.status(500).json({
success: false,
message: 'TBM 세션 완료 처리 중 오류가 발생했습니다.',
error: err.message
});
}
if (result.affectedRows === 0) {
return res.status(404).json({
success: false,
message: 'TBM 세션을 찾을 수 없습니다.'
});
}
res.json({
success: true,
message: 'TBM 세션이 완료되었습니다.'
});
});
},
/**
* TBM 세션 삭제 (draft 상태만)
*/
deleteSession: (req, res) => {
const { sessionId } = req.params;
TbmModel.deleteSession(sessionId, (err, result) => {
if (err) {
console.error('TBM 세션 삭제 오류:', err);
return res.status(500).json({
success: false,
message: 'TBM 세션 삭제 중 오류가 발생했습니다.',
error: err.message
});
}
if (result.affectedRows === 0) {
return res.status(404).json({
success: false,
message: 'TBM 세션을 찾을 수 없거나 이미 완료된 세션입니다.'
});
}
res.json({
success: true,
message: 'TBM 세션이 삭제되었습니다.'
});
});
},
// ==================== 팀 구성 관련 ====================
/**
* 팀원 추가 (작업자별 상세 정보 포함)
*/
addTeamMember: (req, res) => {
const assignmentData = {
session_id: req.params.sessionId,
worker_id: req.body.worker_id,
assigned_role: req.body.assigned_role || null,
work_detail: req.body.work_detail || null,
is_present: req.body.is_present,
absence_reason: req.body.absence_reason || null,
project_id: req.body.project_id || null,
work_type_id: req.body.work_type_id || null,
task_id: req.body.task_id || null,
workplace_category_id: req.body.workplace_category_id || null,
workplace_id: req.body.workplace_id || null,
work_hours: req.body.work_hours !== undefined ? req.body.work_hours : undefined
};
if (!assignmentData.worker_id) {
return res.status(400).json({
success: false,
message: '작업자 ID가 필요합니다.'
});
}
TbmModel.addTeamMember(assignmentData, (err, result) => {
if (err) {
console.error('팀원 추가 오류:', err);
return res.status(500).json({
success: false,
message: '팀원 추가 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({
success: true,
message: '팀원이 추가되었습니다.'
});
});
},
/**
* 분할 항목 추가 (같은 작업자의 추가 배정)
*/
addSplitAssignment: (req, res) => {
const assignmentData = {
session_id: req.params.sessionId,
worker_id: req.body.worker_id,
work_hours: req.body.work_hours,
project_id: req.body.project_id || null,
work_type_id: req.body.work_type_id || null,
task_id: req.body.task_id || null,
workplace_category_id: req.body.workplace_category_id || null,
workplace_id: req.body.workplace_id || null
};
if (!assignmentData.worker_id || !assignmentData.work_hours) {
return res.status(400).json({ success: false, message: '작업자 ID와 작업시간이 필요합니다.' });
}
TbmModel.addSplitAssignment(assignmentData, (err, result) => {
if (err) {
console.error('분할 항목 추가 오류:', err);
return res.status(500).json({ success: false, message: '분할 항목 추가 중 오류가 발생했습니다.' });
}
res.json({ success: true, data: result });
});
},
/**
* 팀 구성 일괄 추가
*/
addTeamMembers: (req, res) => {
const { sessionId } = req.params;
const { members } = req.body;
if (!Array.isArray(members) || members.length === 0) {
return res.status(400).json({
success: false,
message: '팀원 목록이 필요합니다.'
});
}
TbmModel.addTeamMembers(sessionId, members, (err, result) => {
if (err) {
console.error('팀 구성 일괄 추가 오류:', err);
return res.status(500).json({
success: false,
message: '팀 구성 추가 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({
success: true,
message: `${members.length}명의 팀원이 추가되었습니다.`,
data: { count: members.length }
});
});
},
/**
* TBM 세션의 팀 구성 조회
*/
getTeamMembers: (req, res) => {
const { sessionId } = req.params;
TbmModel.getTeamMembers(sessionId, (err, results) => {
if (err) {
console.error('팀 구성 조회 오류:', err);
return res.status(500).json({
success: false,
message: '팀 구성 조회 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({
success: true,
data: results
});
});
},
/**
* 팀원 제거
*/
removeTeamMember: (req, res) => {
const { sessionId, workerId } = req.params;
TbmModel.removeTeamMember(sessionId, workerId, (err, result) => {
if (err) {
console.error('팀원 제거 오류:', err);
return res.status(500).json({
success: false,
message: '팀원 제거 중 오류가 발생했습니다.',
error: err.message
});
}
if (result.affectedRows === 0) {
return res.status(404).json({
success: false,
message: '팀원을 찾을 수 없습니다.'
});
}
res.json({
success: true,
message: '팀원이 제거되었습니다.'
});
});
},
/**
* 세션의 모든 팀원 삭제 (수정 시 사용)
*/
clearAllTeamMembers: (req, res) => {
const { sessionId } = req.params;
TbmModel.clearAllTeamMembers(sessionId, (err, result) => {
if (err) {
console.error('팀원 전체 삭제 오류:', err);
return res.status(500).json({
success: false,
message: '팀원 전체 삭제 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({
success: true,
message: '모든 팀원이 삭제되었습니다.',
data: { deletedCount: result.affectedRows }
});
});
},
// ==================== 안전 체크리스트 관련 ====================
/**
* 모든 안전 체크 항목 조회
*/
getAllSafetyChecks: (req, res) => {
TbmModel.getAllSafetyChecks((err, results) => {
if (err) {
console.error('안전 체크 항목 조회 오류:', err);
return res.status(500).json({
success: false,
message: '안전 체크 항목 조회 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({
success: true,
data: results
});
});
},
/**
* TBM 세션의 안전 체크 기록 조회
*/
getSafetyRecords: (req, res) => {
const { sessionId } = req.params;
TbmModel.getSafetyRecords(sessionId, (err, results) => {
if (err) {
console.error('안전 체크 기록 조회 오류:', err);
return res.status(500).json({
success: false,
message: '안전 체크 기록 조회 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({
success: true,
data: results
});
});
},
/**
* 안전 체크 일괄 저장
*/
saveSafetyRecords: (req, res) => {
const { sessionId } = req.params;
const { records } = req.body;
if (!Array.isArray(records) || records.length === 0) {
return res.status(400).json({
success: false,
message: '안전 체크 기록이 필요합니다.'
});
}
const checkedBy = req.user.user_id;
TbmModel.saveSafetyRecords(sessionId, records, checkedBy, (err, result) => {
if (err) {
console.error('안전 체크 저장 오류:', err);
return res.status(500).json({
success: false,
message: '안전 체크 저장 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({
success: true,
message: '안전 체크가 저장되었습니다.',
data: { count: records.length }
});
});
},
// ==================== 필터링된 안전 체크리스트 (확장) ====================
/**
* 세션에 맞는 필터링된 안전 체크 항목 조회
* 기본 + 날씨 + 작업별 체크항목 통합
*/
getFilteredSafetyChecks: async (req, res) => {
const { sessionId } = req.params;
try {
// 날씨 정보 확인 (이미 저장된 경우 사용, 없으면 새로 조회)
const weatherService = require('../services/weatherService');
let weatherRecord = await weatherService.getWeatherRecord(sessionId);
let weatherConditions = [];
if (weatherRecord && weatherRecord.weather_conditions) {
weatherConditions = weatherRecord.weather_conditions;
} else {
// 날씨 정보가 없으면 현재 날씨 조회
const currentWeather = await weatherService.getCurrentWeather();
weatherConditions = await weatherService.determineWeatherConditions(currentWeather);
// 날씨 기록 저장
await weatherService.saveWeatherRecord(sessionId, currentWeather, weatherConditions);
}
TbmModel.getFilteredSafetyChecks(sessionId, weatherConditions, (err, results) => {
if (err) {
console.error('필터링된 안전 체크 조회 오류:', err);
return res.status(500).json({
success: false,
message: '안전 체크리스트 조회 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({
success: true,
data: results
});
});
} catch (error) {
console.error('필터링된 안전 체크 조회 오류:', error);
res.status(500).json({
success: false,
message: '안전 체크리스트 조회 중 오류가 발생했습니다.',
error: error.message
});
}
},
/**
* 현재 날씨 조회
*/
getCurrentWeather: async (req, res) => {
try {
const weatherService = require('../services/weatherService');
const { nx, ny } = req.query;
const weatherData = await weatherService.getCurrentWeather(nx, ny);
const conditions = await weatherService.determineWeatherConditions(weatherData);
const conditionList = await weatherService.getWeatherConditionList();
// 현재 조건의 상세 정보 매핑
const activeConditions = conditionList.filter(c => conditions.includes(c.condition_code));
res.json({
success: true,
data: {
...weatherData,
conditions,
conditionDetails: activeConditions
}
});
} catch (error) {
console.error('날씨 조회 오류:', error);
res.status(500).json({
success: false,
message: '날씨 조회 중 오류가 발생했습니다.',
error: error.message
});
}
},
/**
* 세션 날씨 정보 저장
*/
saveSessionWeather: async (req, res) => {
const { sessionId } = req.params;
const { weatherConditions } = req.body;
try {
const weatherService = require('../services/weatherService');
// 현재 날씨 조회
const weatherData = await weatherService.getCurrentWeather();
const conditions = weatherConditions || await weatherService.determineWeatherConditions(weatherData);
// 저장
await weatherService.saveWeatherRecord(sessionId, weatherData, conditions);
res.json({
success: true,
message: '날씨 정보가 저장되었습니다.',
data: { conditions }
});
} catch (error) {
console.error('날씨 저장 오류:', error);
res.status(500).json({
success: false,
message: '날씨 저장 중 오류가 발생했습니다.',
error: error.message
});
}
},
/**
* 세션 날씨 정보 조회
*/
getSessionWeather: async (req, res) => {
const { sessionId } = req.params;
try {
const weatherService = require('../services/weatherService');
const weatherRecord = await weatherService.getWeatherRecord(sessionId);
if (!weatherRecord) {
return res.status(404).json({
success: false,
message: '날씨 기록이 없습니다.'
});
}
res.json({
success: true,
data: weatherRecord
});
} catch (error) {
console.error('날씨 조회 오류:', error);
res.status(500).json({
success: false,
message: '날씨 조회 중 오류가 발생했습니다.',
error: error.message
});
}
},
/**
* 날씨 조건 목록 조회
*/
getWeatherConditions: async (req, res) => {
try {
const weatherService = require('../services/weatherService');
const conditions = await weatherService.getWeatherConditionList();
res.json({
success: true,
data: conditions
});
} catch (error) {
console.error('날씨 조건 조회 오류:', error);
res.status(500).json({
success: false,
message: '날씨 조건 조회 중 오류가 발생했습니다.',
error: error.message
});
}
},
// ==================== 안전 체크항목 관리 (관리자용) ====================
/**
* 안전 체크 항목 생성
*/
createSafetyCheck: (req, res) => {
const checkData = req.body;
if (!checkData.check_category || !checkData.check_item) {
return res.status(400).json({
success: false,
message: '카테고리와 체크 항목은 필수입니다.'
});
}
TbmModel.createSafetyCheck(checkData, (err, result) => {
if (err) {
console.error('안전 체크 항목 생성 오류:', err);
return res.status(500).json({
success: false,
message: '안전 체크 항목 생성 중 오류가 발생했습니다.',
error: err.message
});
}
res.status(201).json({
success: true,
message: '안전 체크 항목이 생성되었습니다.',
data: { check_id: result.insertId }
});
});
},
/**
* 안전 체크 항목 수정
*/
updateSafetyCheck: (req, res) => {
const { checkId } = req.params;
const checkData = req.body;
TbmModel.updateSafetyCheck(checkId, checkData, (err, result) => {
if (err) {
console.error('안전 체크 항목 수정 오류:', err);
return res.status(500).json({
success: false,
message: '안전 체크 항목 수정 중 오류가 발생했습니다.',
error: err.message
});
}
if (result.affectedRows === 0) {
return res.status(404).json({
success: false,
message: '안전 체크 항목을 찾을 수 없습니다.'
});
}
res.json({
success: true,
message: '안전 체크 항목이 수정되었습니다.'
});
});
},
/**
* 안전 체크 항목 삭제 (비활성화)
*/
deleteSafetyCheck: (req, res) => {
const { checkId } = req.params;
TbmModel.deleteSafetyCheck(checkId, (err, result) => {
if (err) {
console.error('안전 체크 항목 삭제 오류:', err);
return res.status(500).json({
success: false,
message: '안전 체크 항목 삭제 중 오류가 발생했습니다.',
error: err.message
});
}
if (result.affectedRows === 0) {
return res.status(404).json({
success: false,
message: '안전 체크 항목을 찾을 수 없습니다.'
});
}
res.json({
success: true,
message: '안전 체크 항목이 삭제되었습니다.'
});
});
},
// ==================== 작업 인계 관련 ====================
/**
* 작업 인계 생성
*/
createHandover: (req, res) => {
const handoverData = {
session_id: req.body.session_id,
from_leader_id: req.body.from_leader_id,
to_leader_id: req.body.to_leader_id,
handover_date: req.body.handover_date,
handover_time: req.body.handover_time || null,
reason: req.body.reason,
handover_notes: req.body.handover_notes || null,
worker_ids: req.body.worker_ids || []
};
// 필수 필드 검증
if (!handoverData.session_id || !handoverData.from_leader_id ||
!handoverData.to_leader_id || !handoverData.handover_date || !handoverData.reason) {
return res.status(400).json({
success: false,
message: '필수 정보가 누락되었습니다.'
});
}
TbmModel.createHandover(handoverData, (err, result) => {
if (err) {
console.error('작업 인계 생성 오류:', err);
return res.status(500).json({
success: false,
message: '작업 인계 생성 중 오류가 발생했습니다.',
error: err.message
});
}
res.status(201).json({
success: true,
message: '작업 인계가 생성되었습니다.',
data: { handover_id: result.insertId }
});
});
},
/**
* 작업 인계 확인
*/
confirmHandover: (req, res) => {
const { handoverId } = req.params;
const confirmedBy = req.user.user_id;
TbmModel.confirmHandover(handoverId, confirmedBy, (err, result) => {
if (err) {
console.error('작업 인계 확인 오류:', err);
return res.status(500).json({
success: false,
message: '작업 인계 확인 중 오류가 발생했습니다.',
error: err.message
});
}
if (result.affectedRows === 0) {
return res.status(404).json({
success: false,
message: '작업 인계 건을 찾을 수 없습니다.'
});
}
res.json({
success: true,
message: '작업 인계가 확인되었습니다.'
});
});
},
/**
* 특정 날짜의 작업 인계 목록 조회
*/
getHandoversByDate: (req, res) => {
const { date } = req.params;
TbmModel.getHandoversByDate(date, (err, results) => {
if (err) {
console.error('작업 인계 목록 조회 오류:', err);
return res.status(500).json({
success: false,
message: '작업 인계 목록 조회 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({
success: true,
data: results
});
});
},
/**
* 나에게 온 미확인 인계 건 조회
*/
getMyPendingHandovers: (req, res) => {
// worker_id는 req.user에서 가져옴
const toLeaderId = req.user.worker_id;
if (!toLeaderId) {
return res.status(400).json({
success: false,
message: '작업자 정보를 찾을 수 없습니다.'
});
}
TbmModel.getPendingHandovers(toLeaderId, (err, results) => {
if (err) {
console.error('미확인 인계 건 조회 오류:', err);
return res.status(500).json({
success: false,
message: '미확인 인계 건 조회 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({
success: true,
data: results
});
});
},
// ==================== 통계 및 리포트 ====================
/**
* TBM 통계 조회
*/
getTbmStatistics: (req, res) => {
const { startDate, endDate } = req.query;
if (!startDate || !endDate) {
return res.status(400).json({
success: false,
message: '시작일과 종료일이 필요합니다.'
});
}
TbmModel.getTbmStatistics(startDate, endDate, (err, results) => {
if (err) {
console.error('TBM 통계 조회 오류:', err);
return res.status(500).json({
success: false,
message: 'TBM 통계 조회 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({
success: true,
data: results
});
});
},
/**
* 리더별 TBM 진행 현황 조회
*/
getLeaderStatistics: (req, res) => {
const { startDate, endDate } = req.query;
if (!startDate || !endDate) {
return res.status(400).json({
success: false,
message: '시작일과 종료일이 필요합니다.'
});
}
TbmModel.getLeaderStatistics(startDate, endDate, (err, results) => {
if (err) {
console.error('리더 통계 조회 오류:', err);
return res.status(500).json({
success: false,
message: '리더 통계 조회 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({
success: true,
data: results
});
});
},
// ==================== 작업자 이동 관련 ====================
/**
* 이동 실행 (보내기/빼오기)
*/
createTransfer: (req, res) => {
const { transfer_type, worker_id, source_session_id, dest_session_id, hours,
project_id, work_type_id, task_id, workplace_category_id, workplace_id } = req.body;
if (!transfer_type || !worker_id || !source_session_id || !dest_session_id || !hours) {
return res.status(400).json({
success: false,
message: '필수 정보가 누락되었습니다.'
});
}
const today = new Date();
const transferDate = today.getFullYear() + '-' +
String(today.getMonth() + 1).padStart(2, '0') + '-' +
String(today.getDate()).padStart(2, '0');
const transferData = {
transfer_type, worker_id, source_session_id, dest_session_id,
hours, initiated_by: req.user.user_id, transfer_date: transferDate,
project_id, work_type_id, task_id, workplace_category_id, workplace_id
};
TbmTransferModel.createTransfer(transferData, (err, result) => {
if (err) {
console.error('이동 실행 오류:', err);
return res.status(500).json({
success: false,
message: '이동 실행 중 오류가 발생했습니다.',
error: err.message
});
}
if (!result.success) {
return res.status(400).json(result);
}
res.json({
success: true,
message: '이동이 완료되었습니다.',
data: result
});
});
},
/**
* 당일 이동 내역 조회
*/
getTransfersByDate: (req, res) => {
const { date } = req.params;
TbmTransferModel.getTransfersByDate(date, (err, results) => {
if (err) {
console.error('이동 내역 조회 오류:', err);
return res.status(500).json({
success: false,
message: '이동 내역 조회 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({ success: true, data: results });
});
},
/**
* 이동 취소 (원복)
*/
cancelTransfer: (req, res) => {
const { transferId } = req.params;
TbmTransferModel.cancelTransfer(transferId, (err, result) => {
if (err) {
console.error('이동 취소 오류:', err);
return res.status(500).json({
success: false,
message: '이동 취소 중 오류가 발생했습니다.',
error: err.message
});
}
if (!result.success) {
return res.status(400).json(result);
}
res.json({
success: true,
message: '이동이 취소되었습니다.'
});
});
},
/**
* 당일 전 작업자 배정 현황
*/
getWorkerAssignmentsByDate: (req, res) => {
const { date } = req.params;
TbmTransferModel.getWorkerAssignmentsByDate(date, (err, results) => {
if (err) {
console.error('배정 현황 조회 오류:', err);
return res.status(500).json({
success: false,
message: '배정 현황 조회 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({ success: true, data: results });
});
},
/**
* 작업보고서가 작성되지 않은 TBM 팀 배정 조회
*/
getIncompleteWorkReports: (req, res) => {
const userId = req.user.user_id;
const accessLevel = req.user.access_level;
// 관리자는 모든 TBM 조회, 일반 사용자는 본인이 작성한 것만 조회
const filterUserId = (accessLevel === 'system' || accessLevel === 'admin') ? null : userId;
TbmModel.getIncompleteWorkReports(filterUserId, (err, results) => {
if (err) {
console.error('미완료 작업보고서 조회 오류:', err);
return res.status(500).json({
success: false,
message: '미완료 작업보고서 조회 중 오류가 발생했습니다.',
error: err.message
});
}
res.json({
success: true,
data: results
});
});
}
};
module.exports = TbmController;