From 0a712813e2162ab36737854276ee92a964f8c030 Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Fri, 13 Mar 2026 15:21:30 +0900 Subject: [PATCH] =?UTF-8?q?fix(tkpurchase):=20=ED=98=91=EB=A0=A5=EC=97=85?= =?UTF-8?q?=EC=B2=B4=20=ED=8F=AC=ED=83=88=20=ED=99=9C=EC=84=B1=20=EC=9D=BC?= =?UTF-8?q?=EC=A0=95=20=EC=A0=84=EC=B2=B4=20=ED=91=9C=EC=8B=9C=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 오늘 날짜 범위 필터 제거 → 마감/취소되지 않은 모든 일정 표시. 체크인 날짜 제한도 상태 기반 검증으로 변경하여 일정 기간 외에도 체크인 가능. Co-Authored-By: Claude Opus 4.6 --- .../api/controllers/checkinController.js | 14 ++++---------- .../api/controllers/scheduleController.js | 2 +- tkpurchase/api/models/checkinModel.js | 15 ++++++++++++++- tkpurchase/api/models/scheduleModel.js | 15 ++++++++++++++- tkpurchase/web/partner-portal.html | 4 ++-- .../web/static/js/tkpurchase-partner-portal.js | 18 ++++++++++++++---- 6 files changed, 49 insertions(+), 19 deletions(-) diff --git a/tkpurchase/api/controllers/checkinController.js b/tkpurchase/api/controllers/checkinController.js index 2c7c3d4..c1e370e 100644 --- a/tkpurchase/api/controllers/checkinController.js +++ b/tkpurchase/api/controllers/checkinController.js @@ -20,7 +20,7 @@ async function myCheckins(req, res) { if (!companyId) { return res.status(403).json({ success: false, error: '협력업체 계정이 아닙니다' }); } - const rows = await checkinModel.findTodayByCompany(companyId); + const rows = await checkinModel.findActiveByCompany(companyId); res.json({ success: true, data: rows }); } catch (err) { console.error('Checkin myCheckins error:', err); @@ -39,19 +39,13 @@ async function checkIn(req, res) { if (!resolvedCompanyId) { return res.status(400).json({ success: false, error: '업체 정보가 필요합니다' }); } - // 일정 기간 내 체크인 검증 + // 일정 유효성 검증 const schedule = await scheduleModel.findById(schedule_id); if (!schedule) { return res.status(404).json({ success: false, error: '일정을 찾을 수 없습니다' }); } - const today = new Date(); - today.setHours(0, 0, 0, 0); - const startDate = new Date(schedule.start_date); - startDate.setHours(0, 0, 0, 0); - const endDate = new Date(schedule.end_date); - endDate.setHours(0, 0, 0, 0); - if (today < startDate || today > endDate) { - return res.status(400).json({ success: false, error: '오늘은 해당 일정의 작업 기간이 아닙니다' }); + if (['cancelled', 'rejected', 'completed'].includes(schedule.status)) { + return res.status(400).json({ success: false, error: '마감되었거나 취소된 일정입니다' }); } const data = { schedule_id, diff --git a/tkpurchase/api/controllers/scheduleController.js b/tkpurchase/api/controllers/scheduleController.js index 7502f5a..7480aaf 100644 --- a/tkpurchase/api/controllers/scheduleController.js +++ b/tkpurchase/api/controllers/scheduleController.js @@ -41,7 +41,7 @@ async function mySchedules(req, res) { return res.status(403).json({ success: false, error: '협력업체 계정이 아닙니다' }); } const [schedules, requests] = await Promise.all([ - scheduleModel.findByCompanyToday(companyId), + scheduleModel.findActiveByCompany(companyId), scheduleModel.findRequestsByCompany(companyId) ]); res.json({ success: true, data: { schedules, requests } }); diff --git a/tkpurchase/api/models/checkinModel.js b/tkpurchase/api/models/checkinModel.js index 75acaef..7f1baaa 100644 --- a/tkpurchase/api/models/checkinModel.js +++ b/tkpurchase/api/models/checkinModel.js @@ -216,4 +216,17 @@ async function findHistoryByCompany(companyId, { dateFrom, dateTo, page = 1, lim return { data: checkins, total, page, limit }; } -module.exports = { findBySchedule, findById, findTodayByCompany, checkIn, checkOut, update, resetCheckout, countActive, deleteCheckin, checkOutWithReport, findHistoryByCompany }; +async function findActiveByCompany(companyId) { + const db = getPool(); + const [rows] = await db.query( + `SELECT pc.*, ps.work_description, ps.workplace_name, + (SELECT COUNT(*) FROM partner_work_reports WHERE checkin_id = pc.id) AS work_report_count + FROM partner_work_checkins pc + LEFT JOIN partner_schedules ps ON pc.schedule_id = ps.id + WHERE pc.company_id = ? + AND (pc.check_out_time IS NULL OR DATE(pc.check_in_time) = CURDATE()) + ORDER BY pc.check_in_time DESC`, [companyId]); + return rows; +} + +module.exports = { findBySchedule, findById, findTodayByCompany, findActiveByCompany, checkIn, checkOut, update, resetCheckout, countActive, deleteCheckin, checkOutWithReport, findHistoryByCompany }; diff --git a/tkpurchase/api/models/scheduleModel.js b/tkpurchase/api/models/scheduleModel.js index b271873..fc01e0c 100644 --- a/tkpurchase/api/models/scheduleModel.js +++ b/tkpurchase/api/models/scheduleModel.js @@ -117,4 +117,17 @@ async function deleteSchedule(id) { await db.query('DELETE FROM partner_schedules WHERE id = ?', [id]); } -module.exports = { findAll, findById, findByCompanyToday, findRequestsByCompany, findByProject, create, update, updateStatus, deleteSchedule }; +async function findActiveByCompany(companyId) { + const db = getPool(); + const [rows] = await db.query( + `SELECT ps.*, pc.company_name, p.project_name, p.job_no + FROM partner_schedules ps + LEFT JOIN partner_companies pc ON ps.company_id = pc.id + LEFT JOIN projects p ON ps.project_id = p.project_id + WHERE ps.company_id = ? + AND ps.status NOT IN ('cancelled','rejected','requested','completed') + ORDER BY ps.start_date ASC, ps.created_at DESC`, [companyId]); + return rows; +} + +module.exports = { findAll, findById, findByCompanyToday, findActiveByCompany, findRequestsByCompany, findByProject, create, update, updateStatus, deleteSchedule }; diff --git a/tkpurchase/web/partner-portal.html b/tkpurchase/web/partner-portal.html index 3cf1da9..a63ec9f 100644 --- a/tkpurchase/web/partner-portal.html +++ b/tkpurchase/web/partner-portal.html @@ -35,7 +35,7 @@

-

-

오늘의 작업 일정을 확인하세요.

+

작업 일정을 확인하세요.

@@ -82,7 +82,7 @@ - + diff --git a/tkpurchase/web/static/js/tkpurchase-partner-portal.js b/tkpurchase/web/static/js/tkpurchase-partner-portal.js index 5254f8a..fdcf38a 100644 --- a/tkpurchase/web/static/js/tkpurchase-partner-portal.js +++ b/tkpurchase/web/static/js/tkpurchase-partner-portal.js @@ -25,7 +25,7 @@ async function loadMyCheckins() { const list = r.data || []; portalCheckins = {}; list.forEach(c => { - if (c.schedule_id) portalCheckins[c.schedule_id] = c; + if (c.schedule_id && !portalCheckins[c.schedule_id]) portalCheckins[c.schedule_id] = c; }); } catch(e) { console.warn('Load checkins error:', e); @@ -91,7 +91,7 @@ async function renderScheduleCards() { } else { requestCardsEl.classList.add('hidden'); workRequestFormEl.classList.remove('hidden'); - workRequestFormEl.querySelector('p').textContent = '오늘 예정된 작업 일정이 없습니다. 작업이 필요하시면 아래에서 신청해주세요.'; + workRequestFormEl.querySelector('p').textContent = '등록된 작업 일정이 없습니다. 작업이 필요하시면 아래에서 신청해주세요.'; } // 기본 날짜 설정 const today = new Date().toISOString().substring(0, 10); @@ -104,22 +104,32 @@ async function renderScheduleCards() { requestCardsEl.classList.add('hidden'); workRequestFormEl.classList.add('hidden'); + const today = new Date().toISOString().substring(0, 10); + container.innerHTML = portalSchedules.map(s => { const checkin = portalCheckins[s.id]; const isCheckedIn = checkin && !checkin.check_out_time; const isCheckedOut = checkin && checkin.check_out_time; + const sStart = s.start_date ? s.start_date.substring(0, 10) : ''; + const sEnd = s.end_date ? s.end_date.substring(0, 10) : ''; + const isToday = sStart <= today && sEnd >= today; // 2-step indicators const step1Class = checkin ? 'text-emerald-600' : 'text-gray-400'; const step2Class = isCheckedOut ? 'text-emerald-600' : 'text-gray-400'; - return `
+ const dateBadge = isToday + ? `오늘` + : `${formatDate(s.start_date) === formatDate(s.end_date) ? formatDate(s.start_date) : formatDate(s.start_date) + ' ~ ' + formatDate(s.end_date)}`; + + return `

${escapeHtml(s.workplace_name || '작업장 미지정')}

- ${formatDate(s.start_date) === formatDate(s.end_date) ? formatDate(s.start_date) : formatDate(s.start_date) + ' ~ ' + formatDate(s.end_date)} + ${dateBadge}
+ ${!isToday ? `
${formatDate(s.start_date)}${formatDate(s.start_date) !== formatDate(s.end_date) ? ' ~ ' + formatDate(s.end_date) : ''}
` : ''} ${s.work_description ? `

${escapeHtml(s.work_description)}

` : ''}
예상 ${s.expected_workers || 0}명