diff --git a/tksupport/api/controllers/vacationDashboardController.js b/tksupport/api/controllers/vacationDashboardController.js index de9eb22..6b231f7 100644 --- a/tksupport/api/controllers/vacationDashboardController.js +++ b/tksupport/api/controllers/vacationDashboardController.js @@ -51,11 +51,12 @@ const vacationDashboardController = { async getYearlyOverview(req, res) { try { const year = parseInt(req.query.year) || new Date().getFullYear(); - const [users, balances] = await Promise.all([ + const [users, balances, companyDeductions] = await Promise.all([ vacationDashboardModel.getYearlyOverview(year), - vacationDashboardModel.getBalances(year) + vacationDashboardModel.getBalances(year), + vacationDashboardModel.getCompanyDeductions(year) ]); - res.json({ success: true, data: { users, balances } }); + res.json({ success: true, data: { users, balances, companyDeductions } }); } catch (error) { console.error('연간 총괄 조회 오류:', error); res.status(500).json({ success: false, error: '서버 오류가 발생했습니다' }); diff --git a/tksupport/api/models/vacationDashboardModel.js b/tksupport/api/models/vacationDashboardModel.js index 41559a9..059853c 100644 --- a/tksupport/api/models/vacationDashboardModel.js +++ b/tksupport/api/models/vacationDashboardModel.js @@ -56,7 +56,7 @@ const vacationDashboardModel = { const db = getPool(); const [rows] = await db.query(` SELECT - su.user_id, su.name, su.username, + su.user_id, su.name, su.username, su.hire_date, COALESCE(d.department_id, 0) as department_id, COALESCE(d.department_name, '미배정') as department_name, MONTH(vr.start_date) as month, @@ -104,11 +104,24 @@ const vacationDashboardModel = { return rows; }, + // View 1: 전사 휴가 차감 내역 + async getCompanyDeductions(year) { + const db = getPool(); + const [rows] = await db.query(` + SELECT holiday_date, holiday_name, MONTH(holiday_date) as month + FROM company_holidays + WHERE YEAR(holiday_date) = ? + AND holiday_type = 'ANNUAL_DEDUCT' + AND deduction_applied_at IS NOT NULL + `, [year]); + return rows; + }, + // View 2: 공휴일 표시용 async getHolidays(year, month) { const db = getPool(); const [rows] = await db.query(` - SELECT holiday_date, holiday_name + SELECT holiday_date, holiday_name, holiday_type, deduction_applied_at FROM company_holidays WHERE YEAR(holiday_date) = ? AND MONTH(holiday_date) = ? `, [year, month]); diff --git a/tksupport/web/vacation-dashboard.html b/tksupport/web/vacation-dashboard.html index 050e2ab..c10c97d 100644 --- a/tksupport/web/vacation-dashboard.html +++ b/tksupport/web/vacation-dashboard.html @@ -212,18 +212,36 @@ } function renderYearlyTable(data) { - const { users: rows, balances: balRows } = data; + const { users: rows, balances: balRows, companyDeductions: deductions } = data; const balMap = {}; balRows.forEach(b => { balMap[b.user_id] = { granted: parseFloat(b.granted || 0), used: parseFloat(b.used || 0) }; }); + // 전사 차감 월별 목록 (holiday_date 포함) + const deductionsByMonth = {}; + (deductions || []).forEach(d => { + if (!deductionsByMonth[d.month]) deductionsByMonth[d.month] = []; + deductionsByMonth[d.month].push(d.holiday_date); + }); + // 직원별 월 데이터 병합 const empMap = {}; rows.forEach(r => { if (!empMap[r.user_id]) { - empMap[r.user_id] = { user_id: r.user_id, name: r.name, username: r.username, department_id: r.department_id, department_name: r.department_name, months: {} }; + empMap[r.user_id] = { user_id: r.user_id, name: r.name, username: r.username, hire_date: r.hire_date, department_id: r.department_id, department_name: r.department_name, months: {} }; } if (r.month !== null) empMap[r.user_id].months[r.month] = parseFloat(r.total_days); }); + + // 전사 차감분 합산 (hire_date <= holiday_date 조건) + Object.values(empMap).forEach(emp => { + Object.entries(deductionsByMonth).forEach(([m, dates]) => { + dates.forEach(hDate => { + if (emp.hire_date && emp.hire_date.substring(0, 10) <= hDate.substring(0, 10)) { + emp.months[m] = (emp.months[m] || 0) + 1; + } + }); + }); + }); const employees = Object.values(empMap); // 부서 필터 @@ -245,12 +263,15 @@ } let html = ''; + let deptIdx = 0; Object.entries(deptGroups).forEach(([deptId, group]) => { group.employees.forEach((emp, idx) => { const bal = balMap[emp.user_id] || { granted: 0, used: 0 }; const remaining = bal.granted - bal.used; const remainClass = remaining <= 3 ? 'text-orange-600 font-bold' : 'text-gray-800'; - html += '