fix(monthly-comparison): 0일 0h 수정 + 관리자 검토 태깅

- getAllStatus: daily_attendance_records JOIN으로 실제 근무일/시간 집계
- vacation_days: vacation_types.deduct_days SUM (반차 0.5 정확 반영)
- admin_checked 컬럼 + POST /admin-check API (upsert 패턴)
- 상태 뱃지 라벨: 미검토/확인요청/수정요청/반려/확인완료

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-01 08:20:27 +09:00
parent 65e5530a6a
commit 10fd65ba9e
6 changed files with 53 additions and 10 deletions

View File

@@ -256,7 +256,7 @@ const MonthlyComparisonModel = {
}
},
// 5. 전체 작업자 확인 현황
// 5. 전체 작업자 확인 현황 (실제 근태 데이터 집계 포함)
async getAllStatus(year, month, departmentId) {
const db = await getDb();
let sql = `
@@ -265,15 +265,29 @@ const MonthlyComparisonModel = {
COALESCE(d.department_name, '미배정') AS department_name,
COALESCE(mwc.status, 'pending') AS status,
mwc.confirmed_at, mwc.rejected_at, mwc.reject_reason,
mwc.total_work_days, mwc.total_work_hours,
mwc.total_overtime_hours, mwc.vacation_days, mwc.mismatch_count
mwc.change_details, COALESCE(mwc.admin_checked, 0) AS admin_checked,
COALESCE(att.work_days, 0) AS total_work_days,
COALESCE(att.work_hours, 0) AS total_work_hours,
COALESCE(att.overtime_hours, 0) AS total_overtime_hours,
COALESCE(att.vac_days, 0) AS vacation_days
FROM workers w
LEFT JOIN departments d ON w.department_id = d.department_id
LEFT JOIN monthly_work_confirmations mwc
ON w.user_id = mwc.user_id AND mwc.year = ? AND mwc.month = ?
WHERE w.status = 'active'
LEFT JOIN (
SELECT dar.user_id,
COUNT(CASE WHEN dar.total_work_hours > 0 THEN 1 END) AS work_days,
COALESCE(SUM(dar.total_work_hours), 0) AS work_hours,
COALESCE(SUM(CASE WHEN dar.total_work_hours > 8 THEN dar.total_work_hours - 8 ELSE 0 END), 0) AS overtime_hours,
COALESCE(SUM(CASE WHEN vt.deduct_days IS NOT NULL THEN vt.deduct_days ELSE 0 END), 0) AS vac_days
FROM daily_attendance_records dar
LEFT JOIN vacation_types vt ON dar.vacation_type_id = vt.id
WHERE YEAR(dar.record_date) = ? AND MONTH(dar.record_date) = ?
GROUP BY dar.user_id
) att ON w.user_id = att.user_id
WHERE w.status = 'active' AND w.user_id IS NOT NULL
`;
const params = [year, month];
const params = [year, month, year, month];
if (departmentId) {
sql += ' AND w.department_id = ?';
params.push(departmentId);
@@ -284,6 +298,17 @@ const MonthlyComparisonModel = {
return rows;
},
// 5b. 관리자 개별 검토 태깅
async adminCheck(userId, year, month, checked, checkedBy) {
const db = await getDb();
await db.query(`
INSERT INTO monthly_work_confirmations (user_id, year, month, status, admin_checked)
VALUES (?, ?, ?, 'pending', ?)
ON DUPLICATE KEY UPDATE admin_checked = ?
`, [userId, year, month, checked ? 1 : 0, checked ? 1 : 0]);
return { admin_checked: checked };
},
// 6. 지원팀 사용자 목록 (알림 수신자)
async getSupportTeamUsers() {
const db = await getDb();