fix: 배포 후 버그 수정 — 테이블명/컬럼명 불일치, navbar active, API 검증 강화, 대시보드 통계 라우트 추가
- checkinModel: partner_checkins → partner_work_checkins, countActive() 추가 - workReportModel: partner_work_reports → daily_work_reports - partner-portal: check_out_at/check_in_at → check_out_time/check_in_time - checkinModel findTodayByCompany: LEFT JOIN has_work_report - tkpurchase-core/tksafety-core: navbar match '' 제거 - checkinController: checkOut에 업무현황 검증, stats() 추가 - workReportController: checkin_id 필수 + schedule 일치 검증 - checkinRoutes: GET / 대시보드 통계 라우트 추가 - nginx.conf: visit.html → tksafety 리다이렉트 - migration-purchase-safety.sql: DDL 동기화 - migration-purchase-safety-patch.sql: 신규 패치 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
const checkinModel = require('../models/checkinModel');
|
||||
const workReportModel = require('../models/workReportModel');
|
||||
|
||||
// 일정별 체크인 목록
|
||||
async function list(req, res) {
|
||||
@@ -56,6 +57,10 @@ async function checkIn(req, res) {
|
||||
// 체크아웃
|
||||
async function checkOut(req, res) {
|
||||
try {
|
||||
const report = await workReportModel.findByCheckin(req.params.id);
|
||||
if (!report) {
|
||||
return res.status(400).json({ success: false, error: '업무현황을 먼저 입력해주세요' });
|
||||
}
|
||||
const row = await checkinModel.checkOut(req.params.id);
|
||||
if (!row) return res.status(404).json({ success: false, error: '체크인 기록을 찾을 수 없습니다' });
|
||||
res.json({ success: true, data: row });
|
||||
@@ -77,4 +82,15 @@ async function update(req, res) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { list, myCheckins, checkIn, checkOut, update };
|
||||
// 대시보드 통계
|
||||
async function stats(req, res) {
|
||||
try {
|
||||
const activeCount = await checkinModel.countActive();
|
||||
res.json({ success: true, total: activeCount });
|
||||
} catch (err) {
|
||||
console.error('Checkin stats error:', err);
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { list, myCheckins, checkIn, checkOut, update, stats };
|
||||
|
||||
@@ -58,18 +58,23 @@ async function myReports(req, res) {
|
||||
// 작업보고 등록
|
||||
async function create(req, res) {
|
||||
try {
|
||||
const { checkin_id, company_id, report_date } = req.body;
|
||||
const { checkin_id, schedule_id, company_id, report_date } = req.body;
|
||||
|
||||
if (!report_date) {
|
||||
return res.status(400).json({ success: false, error: '보고일은 필수입니다' });
|
||||
}
|
||||
|
||||
// checkin_id가 있으면 유효성 검증
|
||||
if (checkin_id) {
|
||||
const checkin = await checkinModel.findById(checkin_id);
|
||||
if (!checkin) {
|
||||
return res.status(400).json({ success: false, error: '유효하지 않은 체크인 ID입니다' });
|
||||
}
|
||||
if (!checkin_id) {
|
||||
return res.status(400).json({ success: false, error: '체크인 ID는 필수입니다' });
|
||||
}
|
||||
|
||||
const checkin = await checkinModel.findById(checkin_id);
|
||||
if (!checkin) {
|
||||
return res.status(400).json({ success: false, error: '유효하지 않은 체크인 ID입니다' });
|
||||
}
|
||||
|
||||
if (schedule_id && checkin.schedule_id !== schedule_id) {
|
||||
return res.status(400).json({ success: false, error: '체크인의 일정 정보가 일치하지 않습니다' });
|
||||
}
|
||||
|
||||
const resolvedCompanyId = company_id || req.user.partner_company_id;
|
||||
|
||||
@@ -4,7 +4,7 @@ async function findBySchedule(scheduleId) {
|
||||
const db = getPool();
|
||||
const [rows] = await db.query(
|
||||
`SELECT pc.*, pco.company_name, su.name AS checked_by_name
|
||||
FROM partner_checkins pc
|
||||
FROM partner_work_checkins pc
|
||||
LEFT JOIN partner_companies pco ON pc.company_id = pco.id
|
||||
LEFT JOIN sso_users su ON pc.checked_by = su.user_id
|
||||
WHERE pc.schedule_id = ?
|
||||
@@ -16,7 +16,7 @@ async function findById(id) {
|
||||
const db = getPool();
|
||||
const [rows] = await db.query(
|
||||
`SELECT pc.*, pco.company_name, su.name AS checked_by_name
|
||||
FROM partner_checkins pc
|
||||
FROM partner_work_checkins pc
|
||||
LEFT JOIN partner_companies pco ON pc.company_id = pco.id
|
||||
LEFT JOIN sso_users su ON pc.checked_by = su.user_id
|
||||
WHERE pc.id = ?`, [id]);
|
||||
@@ -26,9 +26,11 @@ async function findById(id) {
|
||||
async function findTodayByCompany(companyId) {
|
||||
const db = getPool();
|
||||
const [rows] = await db.query(
|
||||
`SELECT pc.*, ps.work_description, ps.workplace_name
|
||||
FROM partner_checkins pc
|
||||
`SELECT pc.*, ps.work_description, ps.workplace_name,
|
||||
(dwr.id IS NOT NULL) AS has_work_report
|
||||
FROM partner_work_checkins pc
|
||||
LEFT JOIN partner_schedules ps ON pc.schedule_id = ps.id
|
||||
LEFT JOIN daily_work_reports dwr ON dwr.checkin_id = pc.id
|
||||
WHERE pc.company_id = ? AND DATE(pc.check_in_time) = CURDATE()
|
||||
ORDER BY pc.check_in_time DESC`, [companyId]);
|
||||
return rows;
|
||||
@@ -37,7 +39,7 @@ async function findTodayByCompany(companyId) {
|
||||
async function checkIn(data) {
|
||||
const db = getPool();
|
||||
const [result] = await db.query(
|
||||
`INSERT INTO partner_checkins (schedule_id, company_id, checked_by, check_in_time, worker_names, actual_worker_count, notes)
|
||||
`INSERT INTO partner_work_checkins (schedule_id, company_id, checked_by, check_in_time, worker_names, actual_worker_count, notes)
|
||||
VALUES (?, ?, ?, NOW(), ?, ?, ?)`,
|
||||
[data.schedule_id, data.company_id, data.checked_by,
|
||||
data.worker_names ? JSON.stringify(data.worker_names) : null,
|
||||
@@ -47,7 +49,7 @@ async function checkIn(data) {
|
||||
|
||||
async function checkOut(id) {
|
||||
const db = getPool();
|
||||
await db.query('UPDATE partner_checkins SET check_out_time = NOW() WHERE id = ? AND check_out_time IS NULL', [id]);
|
||||
await db.query('UPDATE partner_work_checkins SET check_out_time = NOW() WHERE id = ? AND check_out_time IS NULL', [id]);
|
||||
return findById(id);
|
||||
}
|
||||
|
||||
@@ -60,8 +62,16 @@ async function update(id, data) {
|
||||
if (data.notes !== undefined) { fields.push('notes = ?'); values.push(data.notes || null); }
|
||||
if (fields.length === 0) return findById(id);
|
||||
values.push(id);
|
||||
await db.query(`UPDATE partner_checkins SET ${fields.join(', ')} WHERE id = ?`, values);
|
||||
await db.query(`UPDATE partner_work_checkins SET ${fields.join(', ')} WHERE id = ?`, values);
|
||||
return findById(id);
|
||||
}
|
||||
|
||||
module.exports = { findBySchedule, findById, findTodayByCompany, checkIn, checkOut, update };
|
||||
async function countActive() {
|
||||
const db = getPool();
|
||||
const [rows] = await db.query(
|
||||
`SELECT COUNT(*) AS cnt FROM partner_work_checkins
|
||||
WHERE check_out_time IS NULL AND DATE(check_in_time) = CURDATE()`);
|
||||
return rows[0].cnt;
|
||||
}
|
||||
|
||||
module.exports = { findBySchedule, findById, findTodayByCompany, checkIn, checkOut, update, countActive };
|
||||
|
||||
@@ -4,7 +4,7 @@ async function findAll({ company_id, date_from, date_to, schedule_id, confirmed,
|
||||
const db = getPool();
|
||||
let sql = `SELECT wr.*, pc.company_name, ps.work_description AS schedule_description,
|
||||
su_reporter.name AS reporter_name, su_confirmer.name AS confirmed_by_name
|
||||
FROM partner_work_reports wr
|
||||
FROM daily_work_reports wr
|
||||
LEFT JOIN partner_companies pc ON wr.company_id = pc.id
|
||||
LEFT JOIN partner_schedules ps ON wr.schedule_id = ps.id
|
||||
LEFT JOIN sso_users su_reporter ON wr.reporter_id = su_reporter.user_id
|
||||
@@ -30,7 +30,7 @@ async function findById(id) {
|
||||
const [rows] = await db.query(
|
||||
`SELECT wr.*, pc.company_name, ps.work_description AS schedule_description,
|
||||
su_reporter.name AS reporter_name, su_confirmer.name AS confirmed_by_name
|
||||
FROM partner_work_reports wr
|
||||
FROM daily_work_reports wr
|
||||
LEFT JOIN partner_companies pc ON wr.company_id = pc.id
|
||||
LEFT JOIN partner_schedules ps ON wr.schedule_id = ps.id
|
||||
LEFT JOIN sso_users su_reporter ON wr.reporter_id = su_reporter.user_id
|
||||
@@ -43,7 +43,7 @@ async function findByCheckin(checkinId) {
|
||||
const db = getPool();
|
||||
const [rows] = await db.query(
|
||||
`SELECT wr.*, pc.company_name
|
||||
FROM partner_work_reports wr
|
||||
FROM daily_work_reports wr
|
||||
LEFT JOIN partner_companies pc ON wr.company_id = pc.id
|
||||
WHERE wr.checkin_id = ?`, [checkinId]);
|
||||
return rows[0] || null;
|
||||
@@ -52,7 +52,7 @@ async function findByCheckin(checkinId) {
|
||||
async function create(data) {
|
||||
const db = getPool();
|
||||
const [result] = await db.query(
|
||||
`INSERT INTO partner_work_reports (schedule_id, checkin_id, company_id, report_date, reporter_id, actual_workers, work_content, progress_rate, issues, next_plan)
|
||||
`INSERT INTO daily_work_reports (schedule_id, checkin_id, company_id, report_date, reporter_id, actual_workers, work_content, progress_rate, issues, next_plan)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[data.schedule_id || null, data.checkin_id || null, data.company_id,
|
||||
data.report_date, data.reporter_id, data.actual_workers || null,
|
||||
@@ -72,14 +72,14 @@ async function update(id, data) {
|
||||
if (data.next_plan !== undefined) { fields.push('next_plan = ?'); values.push(data.next_plan || null); }
|
||||
if (fields.length === 0) return findById(id);
|
||||
values.push(id);
|
||||
await db.query(`UPDATE partner_work_reports SET ${fields.join(', ')} WHERE id = ?`, values);
|
||||
await db.query(`UPDATE daily_work_reports SET ${fields.join(', ')} WHERE id = ?`, values);
|
||||
return findById(id);
|
||||
}
|
||||
|
||||
async function confirm(id, confirmedBy) {
|
||||
const db = getPool();
|
||||
await db.query(
|
||||
'UPDATE partner_work_reports SET confirmed_by = ?, confirmed_at = NOW() WHERE id = ? AND confirmed_by IS NULL',
|
||||
'UPDATE daily_work_reports SET confirmed_by = ?, confirmed_at = NOW() WHERE id = ? AND confirmed_by IS NULL',
|
||||
[confirmedBy, id]);
|
||||
return findById(id);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ const ctrl = require('../controllers/checkinController');
|
||||
|
||||
router.use(requireAuth);
|
||||
|
||||
router.get('/', ctrl.stats); // dashboard stats
|
||||
router.get('/schedule/:scheduleId', ctrl.list);
|
||||
router.get('/my', ctrl.myCheckins); // partner portal
|
||||
router.post('/', ctrl.checkIn); // partner can do this
|
||||
|
||||
Reference in New Issue
Block a user