fix(tkfb): project_code → job_no 컬럼명 수정 (500 에러 해결)

projects 테이블에 project_code 컬럼이 없고 job_no가 올바른 컬럼명.
백엔드 SQL에서는 pr.job_no AS project_code alias 사용,
프론트 드롭다운에서는 p.job_no로 직접 참조.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-03-17 08:18:49 +09:00
parent adf3a197fd
commit 184cdd6aa8
4 changed files with 13 additions and 13 deletions

View File

@@ -57,7 +57,7 @@ const MeetingModel = {
// 안건 // 안건
const [items] = await db.query(` const [items] = await db.query(`
SELECT ai.*, pr.project_name, pr.project_code, SELECT ai.*, pr.project_name, pr.job_no AS project_code,
ms.milestone_name, ms.milestone_date, ms.milestone_name, ms.milestone_date,
su.name AS responsible_name su.name AS responsible_name
FROM meeting_agenda_items ai FROM meeting_agenda_items ai
@@ -185,7 +185,7 @@ const MeetingModel = {
const db = await getDb(); const db = await getDb();
let sql = ` let sql = `
SELECT ai.*, m.title AS meeting_title, m.meeting_date, SELECT ai.*, m.title AS meeting_title, m.meeting_date,
pr.project_name, pr.project_code, pr.project_name, pr.job_no AS project_code,
su.name AS responsible_name su.name AS responsible_name
FROM meeting_agenda_items ai FROM meeting_agenda_items ai
JOIN meeting_minutes m ON ai.meeting_id = m.meeting_id JOIN meeting_minutes m ON ai.meeting_id = m.meeting_id

View File

@@ -48,7 +48,7 @@ const ScheduleModel = {
async getEntries(filters = {}) { async getEntries(filters = {}) {
const db = await getDb(); const db = await getDb();
let sql = ` let sql = `
SELECT e.*, p.phase_name, p.color AS phase_color, pr.project_name, pr.project_code, SELECT e.*, p.phase_name, p.color AS phase_color, pr.project_name, pr.job_no AS project_code,
su.name AS created_by_name su.name AS created_by_name
FROM schedule_entries e FROM schedule_entries e
JOIN schedule_phases p ON e.phase_id = p.phase_id JOIN schedule_phases p ON e.phase_id = p.phase_id
@@ -66,7 +66,7 @@ const ScheduleModel = {
sql += ' AND ((YEAR(e.start_date) = ? AND MONTH(e.start_date) = ?) OR (YEAR(e.end_date) = ? AND MONTH(e.end_date) = ?))'; sql += ' AND ((YEAR(e.start_date) = ? AND MONTH(e.start_date) = ?) OR (YEAR(e.end_date) = ? AND MONTH(e.end_date) = ?))';
params.push(filters.year, filters.month, filters.year, filters.month); params.push(filters.year, filters.month, filters.year, filters.month);
} }
sql += ' ORDER BY pr.project_code, p.display_order, e.display_order'; sql += ' ORDER BY pr.job_no, p.display_order, e.display_order';
const [rows] = await db.query(sql, params); const [rows] = await db.query(sql, params);
return rows; return rows;
}, },
@@ -76,13 +76,13 @@ const ScheduleModel = {
// 해당 연도에 걸치는 모든 항목 // 해당 연도에 걸치는 모든 항목
const [entries] = await db.query(` const [entries] = await db.query(`
SELECT e.*, p.phase_name, p.color AS phase_color, p.display_order AS phase_order, SELECT e.*, p.phase_name, p.color AS phase_color, p.display_order AS phase_order,
pr.project_name, pr.project_code pr.project_name, pr.job_no AS project_code
FROM schedule_entries e FROM schedule_entries e
JOIN schedule_phases p ON e.phase_id = p.phase_id JOIN schedule_phases p ON e.phase_id = p.phase_id
JOIN projects pr ON e.project_id = pr.project_id JOIN projects pr ON e.project_id = pr.project_id
WHERE (YEAR(e.start_date) <= ? AND YEAR(e.end_date) >= ?) WHERE (YEAR(e.start_date) <= ? AND YEAR(e.end_date) >= ?)
AND e.status != 'cancelled' AND e.status != 'cancelled'
ORDER BY pr.project_code, p.display_order, e.display_order ORDER BY pr.job_no, p.display_order, e.display_order
`, [year, year]); `, [year, year]);
// 의존관계 // 의존관계
@@ -98,7 +98,7 @@ const ScheduleModel = {
// 마일스톤 // 마일스톤
const [milestones] = await db.query(` const [milestones] = await db.query(`
SELECT m.*, pr.project_name, pr.project_code SELECT m.*, pr.project_name, pr.job_no AS project_code
FROM schedule_milestones m FROM schedule_milestones m
JOIN projects pr ON m.project_id = pr.project_id JOIN projects pr ON m.project_id = pr.project_id
WHERE YEAR(m.milestone_date) = ? WHERE YEAR(m.milestone_date) = ?
@@ -200,7 +200,7 @@ const ScheduleModel = {
async getMilestones(filters = {}) { async getMilestones(filters = {}) {
const db = await getDb(); const db = await getDb();
let sql = ` let sql = `
SELECT m.*, pr.project_name, pr.project_code, e.task_name AS entry_task_name, SELECT m.*, pr.project_name, pr.job_no AS project_code, e.task_name AS entry_task_name,
su.name AS created_by_name su.name AS created_by_name
FROM schedule_milestones m FROM schedule_milestones m
JOIN projects pr ON m.project_id = pr.project_id JOIN projects pr ON m.project_id = pr.project_id

View File

@@ -35,7 +35,7 @@ document.addEventListener('DOMContentLoaded', async () => {
// Populate project select in item modal // Populate project select in item modal
const projSel = document.getElementById('itemProject'); const projSel = document.getElementById('itemProject');
projects.forEach(p => { projects.forEach(p => {
projSel.innerHTML += `<option value="${p.project_id}">${escapeHtml(p.project_code)} ${escapeHtml(p.project_name)}</option>`; projSel.innerHTML += `<option value="${p.project_id}">${escapeHtml(p.job_no)} ${escapeHtml(p.project_name)}</option>`;
}); });
// Populate responsible user select // Populate responsible user select

View File

@@ -374,7 +374,7 @@ function showMilestoneDetail(milestoneId) {
function showNcPopup(projectId) { function showNcPopup(projectId) {
const list = ncCache[projectId] || []; const list = ncCache[projectId] || [];
const proj = projects.find(p => p.project_id === projectId); const proj = projects.find(p => p.project_id === projectId);
document.getElementById('ncPopupTitle').textContent = `부적합 현황 - ${proj ? proj.project_code : ''}`; document.getElementById('ncPopupTitle').textContent = `부적합 현황 - ${proj ? proj.job_no : ''}`;
const statusLabels = { reported: '신고', received: '접수', reviewing: '검토중', in_progress: '처리중', completed: '완료' }; const statusLabels = { reported: '신고', received: '접수', reviewing: '검토중', in_progress: '처리중', completed: '완료' };
let html = ''; let html = '';
if (list.length === 0) { if (list.length === 0) {
@@ -402,7 +402,7 @@ function openEntryModal(editId) {
document.getElementById('entryModalTitle').textContent = isEdit ? '공정표 항목 수정' : '공정표 항목 추가'; document.getElementById('entryModalTitle').textContent = isEdit ? '공정표 항목 수정' : '공정표 항목 추가';
// Populate dropdowns // Populate dropdowns
populateSelect('entryProject', projects, 'project_id', p => `${p.project_code} ${p.project_name}`); populateSelect('entryProject', projects, 'project_id', p => `${p.job_no} ${p.project_name}`);
populateSelect('entryPhase', phases, 'phase_id', p => p.phase_name); populateSelect('entryPhase', phases, 'phase_id', p => p.phase_name);
// Template select (populated on phase change) // Template select (populated on phase change)
@@ -530,7 +530,7 @@ async function saveEntry() {
/* ===== Batch Modal ===== */ /* ===== Batch Modal ===== */
function openBatchModal() { function openBatchModal() {
populateSelect('batchProject', projects, 'project_id', p => `${p.project_code} ${p.project_name}`); populateSelect('batchProject', projects, 'project_id', p => `${p.job_no} ${p.project_name}`);
populateSelect('batchPhase', phases, 'phase_id', p => p.phase_name); populateSelect('batchPhase', phases, 'phase_id', p => p.phase_name);
document.getElementById('batchStartDate').value = ''; document.getElementById('batchStartDate').value = '';
document.getElementById('batchTemplateList').innerHTML = ''; document.getElementById('batchTemplateList').innerHTML = '';
@@ -610,7 +610,7 @@ function openMilestoneModal(editId) {
const isEdit = !!editId; const isEdit = !!editId;
document.getElementById('milestoneModalTitle').textContent = isEdit ? '마일스톤 수정' : '마일스톤 추가'; document.getElementById('milestoneModalTitle').textContent = isEdit ? '마일스톤 수정' : '마일스톤 추가';
populateSelect('milestoneProject', projects, 'project_id', p => `${p.project_code} ${p.project_name}`); populateSelect('milestoneProject', projects, 'project_id', p => `${p.job_no} ${p.project_name}`);
if (isEdit) { if (isEdit) {
const m = ganttData.milestones.find(ms => ms.milestone_id === editId); const m = ganttData.milestones.find(ms => ms.milestone_id === editId);