fix(tksupport): 전사 차감 월별 반영 + 테이블 가독성 개선 + 캘린더 차감일 표시
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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: '서버 오류가 발생했습니다' });
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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 += '<tr class="border-b border-gray-100">';
|
||||
const zebraClass = idx % 2 === 1 ? ' bg-gray-50' : '';
|
||||
const deptBorder = idx === 0 && deptIdx > 0 ? ' border-t-2 border-gray-300' : ' border-b border-gray-100';
|
||||
html += `<tr class="${deptBorder}${zebraClass}">`;
|
||||
if (idx === 0) {
|
||||
html += `<td class="font-medium text-gray-700 align-top" rowspan="${group.employees.length}">${escapeHtml(group.name)}</td>`;
|
||||
}
|
||||
@@ -269,6 +290,7 @@
|
||||
html += `<td class="text-center ${remainClass}">${remaining % 1 === 0 ? remaining : remaining.toFixed(1)}</td>`;
|
||||
html += '</tr>';
|
||||
});
|
||||
deptIdx++;
|
||||
});
|
||||
tbody.innerHTML = html;
|
||||
}
|
||||
@@ -308,9 +330,14 @@
|
||||
const container = document.getElementById('calendarContainer');
|
||||
const daysInMonth = new Date(year, month, 0).getDate();
|
||||
const holidaySet = {};
|
||||
const deductionSet = {};
|
||||
holidays.forEach(h => {
|
||||
const d = new Date(h.holiday_date).getDate();
|
||||
holidaySet[d] = h.holiday_name;
|
||||
if (h.holiday_type === 'ANNUAL_DEDUCT' && h.deduction_applied_at) {
|
||||
deductionSet[d] = h.holiday_name;
|
||||
} else {
|
||||
holidaySet[d] = h.holiday_name;
|
||||
}
|
||||
});
|
||||
|
||||
// user_id별 그룹핑
|
||||
@@ -358,9 +385,13 @@
|
||||
const isHoliday = holidaySet[d];
|
||||
const vacType = dayVacation[d];
|
||||
|
||||
const isDeduction = deductionSet[d];
|
||||
if (vacType) {
|
||||
const tc = TYPE_COLOR[vacType] || DEFAULT_TYPE;
|
||||
html += `<div class="cal-cell ${tc.bg} ${tc.text} font-medium" title="${d}일">${tc.label}</div>`;
|
||||
} else if (isDeduction) {
|
||||
const tc = TYPE_COLOR.PAID;
|
||||
html += `<div class="cal-cell ${tc.bg} ${tc.text} font-medium" title="${isDeduction}">${tc.label}</div>`;
|
||||
} else if (isWeekend || isHoliday) {
|
||||
html += `<div class="cal-cell weekend" title="${isHoliday || (dow === 0 ? '일' : '토')}">${d}</div>`;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user