feat(vacation): 조퇴 연차 차감 처리 (0.75일 = 반차+반반차)

- EARLY_LEAVE vacation type 추가 (deduct_days=0.75)
- work-status: isLeave=true + 동적 vacation_type_id 조회 + 실패 보호
- annual-overview: 월별 요약 테이블에 조퇴 컬럼 추가 + 편집 드롭다운

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-03-31 10:17:32 +09:00
parent ec7699b270
commit 408bf1af62
3 changed files with 47 additions and 7 deletions

View File

@@ -430,6 +430,7 @@
let currentModalUserId = null;
let currentModalName = '';
let editBackup = {};
let earlyLeaveVtId = null; // EARLY_LEAVE vacation_type_id (동적 조회)
const DAYS_KR = ['일', '월', '화', '수', '목', '금', '토'];
// 경조사 유형
@@ -477,6 +478,15 @@
document.getElementById('tableBody').innerHTML = '<tr><td colspan="9" class="loading">로딩 중...</td></tr>';
try {
// EARLY_LEAVE 유형 ID 조회 (최초 1회)
if (!earlyLeaveVtId) {
try {
var vtRes = await axios.get('/attendance/vacation-types');
var elType = (vtRes.data.data || []).find(function(t) { return t.type_code === 'EARLY_LEAVE'; });
earlyLeaveVtId = elType ? elType.id : null;
} catch(e) {}
}
// 작업자 로드
const workersRes = await axios.get('/workers?limit=100');
workers = (workersRes.data.data || [])
@@ -761,15 +771,15 @@
// 월별 그룹핑
const monthly = {};
const totals = { ANNUAL_FULL: 0, ANNUAL_HALF: 0, ANNUAL_QUARTER: 0, other: 0 };
const totals = { ANNUAL_FULL: 0, ANNUAL_HALF: 0, ANNUAL_QUARTER: 0, EARLY_LEAVE: 0, other: 0 };
records.forEach(r => {
const d = new Date(r.record_date);
const m = d.getMonth() + 1;
if (!monthly[m]) monthly[m] = { ANNUAL_FULL: 0, ANNUAL_HALF: 0, ANNUAL_QUARTER: 0, other: 0, total: 0 };
if (!monthly[m]) monthly[m] = { ANNUAL_FULL: 0, ANNUAL_HALF: 0, ANNUAL_QUARTER: 0, EARLY_LEAVE: 0, other: 0, total: 0 };
const days = parseFloat(r.vacation_days) || 1;
const code = r.vacation_type_code || 'other';
if (['ANNUAL_FULL', 'ANNUAL_HALF', 'ANNUAL_QUARTER'].includes(code)) {
if (['ANNUAL_FULL', 'ANNUAL_HALF', 'ANNUAL_QUARTER', 'EARLY_LEAVE'].includes(code)) {
monthly[m][code] += days;
totals[code] += days;
} else {
@@ -779,12 +789,12 @@
monthly[m].total += days;
});
const grandTotal = totals.ANNUAL_FULL + totals.ANNUAL_HALF + totals.ANNUAL_QUARTER + totals.other;
const grandTotal = totals.ANNUAL_FULL + totals.ANNUAL_HALF + totals.ANNUAL_QUARTER + totals.EARLY_LEAVE + totals.other;
// 월별 요약 테이블
var html = '<h4 style="font-size:0.85rem;font-weight:600;margin-bottom:0.5rem;">월별 요약</h4>';
html += '<table class="data-table" style="margin-bottom:1rem;"><thead><tr>';
html += '<th style="position:static !important;">월</th><th style="position:static !important;">연차</th><th style="position:static !important;">반차</th><th style="position:static !important;">반반차</th><th style="position:static !important;">합계</th>';
html += '<th style="position:static !important;">월</th><th style="position:static !important;">연차</th><th style="position:static !important;">반차</th><th style="position:static !important;">반반차</th><th style="position:static !important;">조퇴</th><th style="position:static !important;">합계</th>';
html += '</tr></thead><tbody>';
for (var m = 1; m <= 12; m++) {
if (!monthly[m]) continue;
@@ -793,6 +803,7 @@
html += '<td>' + (md.ANNUAL_FULL > 0 ? fmtNum(md.ANNUAL_FULL) : '-') + '</td>';
html += '<td>' + (md.ANNUAL_HALF > 0 ? fmtNum(md.ANNUAL_HALF) : '-') + '</td>';
html += '<td>' + (md.ANNUAL_QUARTER > 0 ? fmtNum(md.ANNUAL_QUARTER) : '-') + '</td>';
html += '<td>' + (md.EARLY_LEAVE > 0 ? fmtNum(md.EARLY_LEAVE) : '-') + '</td>';
html += '<td style="font-weight:600">' + fmtNum(md.total) + '</td></tr>';
}
html += '<tr style="font-weight:700;border-top:2px solid #e5e7eb;">';
@@ -800,6 +811,7 @@
html += '<td>' + fmtNum(totals.ANNUAL_FULL) + '</td>';
html += '<td>' + fmtNum(totals.ANNUAL_HALF) + '</td>';
html += '<td>' + fmtNum(totals.ANNUAL_QUARTER) + '</td>';
html += '<td>' + fmtNum(totals.EARLY_LEAVE) + '</td>';
html += '<td style="color:#059669">' + fmtNum(grandTotal) + '</td></tr>';
html += '</tbody></table>';
@@ -866,6 +878,7 @@
+ '<option value="1"' + (currentTypeId==1?' selected':'') + '>연차</option>'
+ '<option value="2"' + (currentTypeId==2?' selected':'') + '>반차</option>'
+ '<option value="3"' + (currentTypeId==3?' selected':'') + '>반반차</option>'
+ (earlyLeaveVtId ? '<option value="' + earlyLeaveVtId + '"' + (currentTypeId==earlyLeaveVtId?' selected':'') + '>조퇴</option>' : '')
+ '</select>'
+ '<button class="btn-save" onclick="saveEdit(' + recordId + ',\'' + recordDate + '\',' + currentTypeId + ')">저장</button>'
+ '<button class="btn-cancel" onclick="cancelEdit(' + recordId + ')">취소</button>';