fix: 작업 분석에서 공정(대분류)으로 올바르게 분류
문제: work_type_id에 task_id가 저장된 경우 공정 분류가 안됨 - work_type_id=10 → 실제로는 task "노즐 용접" (공정: Vessel) 해결: - API에서 task_id인 경우 해당 task의 work_type_id로 공정 조회 - getRecentWork, getProjectWorkTypeRawData 쿼리 수정 - 프론트엔드는 API 결과의 work_type_name 직접 사용 공정(대분류): Base(구조물), Vessel(용기), Piping Assembly(배관), 작업대기, 휴무, 시설설비 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -182,8 +182,10 @@ class WorkAnalysis {
|
|||||||
|
|
||||||
// 최근 작업 현황
|
// 최근 작업 현황
|
||||||
async getRecentWork(startDate, endDate, limit = 50) {
|
async getRecentWork(startDate, endDate, limit = 50) {
|
||||||
|
// work_type_id가 work_types에 있으면 직접 사용,
|
||||||
|
// 없으면 tasks 테이블을 통해 해당 task의 work_type_id로 공정(대분류) 조회
|
||||||
const query = `
|
const query = `
|
||||||
SELECT
|
SELECT
|
||||||
dwr.id,
|
dwr.id,
|
||||||
dwr.report_date,
|
dwr.report_date,
|
||||||
dwr.worker_id,
|
dwr.worker_id,
|
||||||
@@ -191,8 +193,10 @@ class WorkAnalysis {
|
|||||||
dwr.project_id,
|
dwr.project_id,
|
||||||
p.project_name,
|
p.project_name,
|
||||||
p.job_no,
|
p.job_no,
|
||||||
dwr.work_type_id,
|
dwr.work_type_id as original_work_type_id,
|
||||||
wt.name as work_type_name,
|
COALESCE(wt.id, t.work_type_id) as work_type_id,
|
||||||
|
COALESCE(wt.name, wt2.name) as work_type_name,
|
||||||
|
t.task_name as task_name,
|
||||||
dwr.work_status_id,
|
dwr.work_status_id,
|
||||||
wst.name as work_status_name,
|
wst.name as work_status_name,
|
||||||
dwr.error_type_id,
|
dwr.error_type_id,
|
||||||
@@ -205,6 +209,8 @@ class WorkAnalysis {
|
|||||||
LEFT JOIN workers w ON dwr.worker_id = w.worker_id
|
LEFT JOIN workers w ON dwr.worker_id = w.worker_id
|
||||||
LEFT JOIN projects p ON dwr.project_id = p.project_id
|
LEFT JOIN projects p ON dwr.project_id = p.project_id
|
||||||
LEFT JOIN work_types wt ON dwr.work_type_id = wt.id
|
LEFT JOIN work_types wt ON dwr.work_type_id = wt.id
|
||||||
|
LEFT JOIN tasks t ON dwr.work_type_id = t.task_id
|
||||||
|
LEFT JOIN work_types wt2 ON t.work_type_id = wt2.id
|
||||||
LEFT JOIN work_status_types wst ON dwr.work_status_id = wst.id
|
LEFT JOIN work_status_types wst ON dwr.work_status_id = wst.id
|
||||||
LEFT JOIN error_types et ON dwr.error_type_id = et.id
|
LEFT JOIN error_types et ON dwr.error_type_id = et.id
|
||||||
LEFT JOIN users u ON dwr.created_by = u.user_id
|
LEFT JOIN users u ON dwr.created_by = u.user_id
|
||||||
@@ -224,7 +230,8 @@ class WorkAnalysis {
|
|||||||
project_name: row.project_name || `프로젝트 ${row.project_id}`,
|
project_name: row.project_name || `프로젝트 ${row.project_id}`,
|
||||||
job_no: row.job_no || 'N/A',
|
job_no: row.job_no || 'N/A',
|
||||||
work_type_id: row.work_type_id,
|
work_type_id: row.work_type_id,
|
||||||
work_type_name: row.work_type_name || `작업유형 ${row.work_type_id}`,
|
work_type_name: row.work_type_name || `작업유형 ${row.original_work_type_id}`,
|
||||||
|
task_name: row.task_name || null,
|
||||||
work_status_id: row.work_status_id,
|
work_status_id: row.work_status_id,
|
||||||
work_status_name: row.work_status_name || '정상',
|
work_status_name: row.work_status_name || '정상',
|
||||||
error_type_id: row.error_type_id,
|
error_type_id: row.error_type_id,
|
||||||
@@ -427,15 +434,16 @@ class WorkAnalysis {
|
|||||||
throw new Error(`대시보드 데이터 조회 실패: ${error.message}`);
|
throw new Error(`대시보드 데이터 조회 실패: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 프로젝트별-작업별 시간 분석용 데이터 조회
|
// 프로젝트별-작업별 시간 분석용 데이터 조회 (공정/대분류 기준)
|
||||||
async getProjectWorkTypeRawData(startDate, endDate) {
|
async getProjectWorkTypeRawData(startDate, endDate) {
|
||||||
|
// work_type_id가 실제로 task_id인 경우 해당 task의 work_type_id로 공정 조회
|
||||||
const query = `
|
const query = `
|
||||||
SELECT
|
SELECT
|
||||||
COALESCE(p.project_id, dwr.project_id) as project_id,
|
COALESCE(p.project_id, dwr.project_id) as project_id,
|
||||||
COALESCE(p.project_name, CONCAT('프로젝트 ', dwr.project_id)) as project_name,
|
COALESCE(p.project_name, CONCAT('프로젝트 ', dwr.project_id)) as project_name,
|
||||||
COALESCE(p.job_no, 'N/A') as job_no,
|
COALESCE(p.job_no, 'N/A') as job_no,
|
||||||
dwr.work_type_id,
|
COALESCE(wt.id, t.work_type_id) as work_type_id,
|
||||||
COALESCE(wt.name, CONCAT('작업유형 ', dwr.work_type_id)) as work_type_name,
|
COALESCE(wt.name, wt2.name, CONCAT('작업유형 ', dwr.work_type_id)) as work_type_name,
|
||||||
|
|
||||||
-- 총 시간
|
-- 총 시간
|
||||||
SUM(dwr.work_hours) as total_hours,
|
SUM(dwr.work_hours) as total_hours,
|
||||||
@@ -460,9 +468,13 @@ class WorkAnalysis {
|
|||||||
FROM daily_work_reports dwr
|
FROM daily_work_reports dwr
|
||||||
LEFT JOIN projects p ON dwr.project_id = p.project_id
|
LEFT JOIN projects p ON dwr.project_id = p.project_id
|
||||||
LEFT JOIN work_types wt ON dwr.work_type_id = wt.id
|
LEFT JOIN work_types wt ON dwr.work_type_id = wt.id
|
||||||
|
LEFT JOIN tasks t ON dwr.work_type_id = t.task_id
|
||||||
|
LEFT JOIN work_types wt2 ON t.work_type_id = wt2.id
|
||||||
WHERE dwr.report_date BETWEEN ? AND ?
|
WHERE dwr.report_date BETWEEN ? AND ?
|
||||||
GROUP BY dwr.project_id, p.project_name, p.job_no, dwr.work_type_id, wt.name
|
GROUP BY dwr.project_id, p.project_name, p.job_no,
|
||||||
ORDER BY p.project_name, wt.name
|
COALESCE(wt.id, t.work_type_id),
|
||||||
|
COALESCE(wt.name, wt2.name)
|
||||||
|
ORDER BY p.project_name, work_type_name
|
||||||
`;
|
`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -983,22 +983,6 @@
|
|||||||
return WorkAnalysis.utils.isVacationProject(projectName);
|
return WorkAnalysis.utils.isVacationProject(projectName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 대분류 매핑 함수 (work_type_id → 대분류)
|
|
||||||
function getMajorCategory(workTypeId) {
|
|
||||||
// 대분류 매핑: 1=Base제작, 2=용기제작, 3=파이핑
|
|
||||||
const majorCategories = {
|
|
||||||
1: { id: 1, name: 'Base제작' },
|
|
||||||
2: { id: 2, name: '용기제작' },
|
|
||||||
3: { id: 3, name: '파이핑' },
|
|
||||||
4: { id: 4, name: '작업대기' },
|
|
||||||
11: { id: 11, name: '휴무' },
|
|
||||||
12: { id: 12, name: '시설설비' }
|
|
||||||
};
|
|
||||||
|
|
||||||
// 매핑된 대분류가 있으면 반환, 없으면 기타
|
|
||||||
return majorCategories[workTypeId] || { id: 0, name: '기타' };
|
|
||||||
}
|
|
||||||
|
|
||||||
function getKSTDate() {
|
function getKSTDate() {
|
||||||
return WorkAnalysis.utils.getKSTDate();
|
return WorkAnalysis.utils.getKSTDate();
|
||||||
}
|
}
|
||||||
@@ -1519,20 +1503,20 @@
|
|||||||
vacationData.regularHours += hours;
|
vacationData.regularHours += hours;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 일반 프로젝트 처리 - 대분류로 그룹화
|
// 일반 프로젝트 처리 - API에서 반환된 공정(대분류) 사용
|
||||||
const majorCategory = getMajorCategory(work.work_type_id);
|
|
||||||
const projectName = work.project_name || `프로젝트 ${work.project_id}`;
|
const projectName = work.project_name || `프로젝트 ${work.project_id}`;
|
||||||
|
const workTypeName = work.work_type_name || '기타';
|
||||||
|
|
||||||
// 대분류 기준으로 집계 (프로젝트별로 구분)
|
// 공정(대분류) 기준으로 집계 (프로젝트별로 구분)
|
||||||
const combinedKey = `${work.project_id || 'unknown'}_${majorCategory.id}`;
|
const combinedKey = `${work.project_id || 'unknown'}_${work.work_type_id || 0}`;
|
||||||
|
|
||||||
if (!workTypeMap.has(combinedKey)) {
|
if (!workTypeMap.has(combinedKey)) {
|
||||||
workTypeMap.set(combinedKey, {
|
workTypeMap.set(combinedKey, {
|
||||||
project_id: work.project_id,
|
project_id: work.project_id,
|
||||||
project_name: projectName,
|
project_name: projectName,
|
||||||
job_no: work.job_no,
|
job_no: work.job_no,
|
||||||
work_type_id: majorCategory.id,
|
work_type_id: work.work_type_id,
|
||||||
work_type_name: majorCategory.name,
|
work_type_name: workTypeName,
|
||||||
regularHours: 0,
|
regularHours: 0,
|
||||||
errorHours: 0,
|
errorHours: 0,
|
||||||
errorDetails: new Map(), // 오류 유형별 세분화
|
errorDetails: new Map(), // 오류 유형별 세분화
|
||||||
@@ -1838,14 +1822,14 @@
|
|||||||
|
|
||||||
const project = projectMap.get(projectKey);
|
const project = projectMap.get(projectKey);
|
||||||
|
|
||||||
// 대분류로 그룹화
|
// API에서 반환된 공정(대분류) 사용
|
||||||
const majorCategory = getMajorCategory(work.work_type_id);
|
const workTypeName = work.work_type_name || '기타';
|
||||||
const workTypeKey = `${majorCategory.id}_${majorCategory.name}`;
|
const workTypeKey = `${work.work_type_id || 0}_${workTypeName}`;
|
||||||
|
|
||||||
if (!project.work_types.has(workTypeKey)) {
|
if (!project.work_types.has(workTypeKey)) {
|
||||||
project.work_types.set(workTypeKey, {
|
project.work_types.set(workTypeKey, {
|
||||||
work_type_id: majorCategory.id,
|
work_type_id: work.work_type_id,
|
||||||
work_type_name: majorCategory.name,
|
work_type_name: workTypeName,
|
||||||
total_hours: 0,
|
total_hours: 0,
|
||||||
regular_hours: 0,
|
regular_hours: 0,
|
||||||
error_hours: 0,
|
error_hours: 0,
|
||||||
@@ -2505,14 +2489,13 @@
|
|||||||
workerGroupedData[workerName][projectName] = {};
|
workerGroupedData[workerName][projectName] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 대분류로 작업내용 그룹화
|
// 작업내용 - API에서 공정(대분류) 이름을 직접 반환
|
||||||
let finalWorkTypeName;
|
let finalWorkTypeName;
|
||||||
if (isVacationProject(projectName)) {
|
if (isVacationProject(projectName)) {
|
||||||
finalWorkTypeName = '-'; // 연차/휴무는 작업내용을 '-'로 통합
|
finalWorkTypeName = '-'; // 연차/휴무는 작업내용을 '-'로 통합
|
||||||
} else {
|
} else {
|
||||||
// 대분류 매핑 사용
|
// API에서 반환된 work_type_name 사용 (공정/대분류)
|
||||||
const majorCategory = getMajorCategory(work.work_type_id);
|
finalWorkTypeName = work.work_type_name || '기타';
|
||||||
finalWorkTypeName = majorCategory.name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!workerGroupedData[workerName][projectName][finalWorkTypeName]) {
|
if (!workerGroupedData[workerName][projectName][finalWorkTypeName]) {
|
||||||
|
|||||||
Reference in New Issue
Block a user