fix(tkuser): 장기근속 5년 정확 경과 체크 + 수동부여 연도 입력

- autoGrantLongServiceLeave: 연도 차이 → 정확한 기념일 경과 확인
  (today < anniversaryDate이면 스킵)
- 수동 배정 모달에 "배정 연도" 필드 추가 (탭 연도 의존 제거)
- 장기근속 만료일 = null 유지 (기존 동작 그대로)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-03-31 08:26:43 +09:00
parent a30482ec34
commit b2ce691ef9
3 changed files with 25 additions and 17 deletions

View File

@@ -264,15 +264,17 @@ async function autoGrantLongServiceLeave(users, year, createdBy, settings) {
if (!u.hire_date) continue;
const hire = new Date(u.hire_date);
const yearsWorked = year - hire.getFullYear();
// 해당 연도 내 threshold 도래 건만 (소급 없음)
if (yearsWorked !== thresholdYears) continue;
// 기념일이 해당 연도인지 확인
// 정확한 5년 기념일 계산
const anniversaryDate = new Date(hire);
anniversaryDate.setFullYear(hire.getFullYear() + thresholdYears);
// 기념일이 해당 연도가 아니면 스킵
if (anniversaryDate.getFullYear() !== year) continue;
// 기념일이 아직 도래하지 않았으면 스킵 (정확히 5년 경과 필요)
const today = new Date();
if (today < anniversaryDate) continue;
// long_service_excluded 체크
const [userRows] = await db.query(
'SELECT long_service_excluded FROM sso_users WHERE user_id = ?',

View File

@@ -904,15 +904,21 @@
<option value="">선택</option>
</select>
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">배정 유형</label>
<select id="vbBalanceType" class="input-field w-full px-3 py-1.5 rounded-lg text-sm" onchange="onBalanceTypeChange()">
<option value="AUTO">기본연차</option>
<option value="MANUAL">추가부여</option>
<option value="CARRY_OVER">이월연차</option>
<option value="LONG_SERVICE">장기근속</option>
<option value="COMPANY_GRANT">회사부여</option>
</select>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">배정 유형</label>
<select id="vbBalanceType" class="input-field w-full px-3 py-1.5 rounded-lg text-sm" onchange="onBalanceTypeChange()">
<option value="AUTO">기본연차</option>
<option value="MANUAL">추가부여</option>
<option value="CARRY_OVER">이월연차</option>
<option value="LONG_SERVICE">장기근속</option>
<option value="COMPANY_GRANT">회사부여</option>
</select>
</div>
<div>
<label class="block text-xs font-medium text-gray-600 mb-1">배정 연도 <span class="text-red-400">*</span></label>
<input type="number" id="vbYear" class="input-field w-full px-3 py-1.5 rounded-lg text-sm" min="2020" max="2035" required>
</div>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
@@ -2410,7 +2416,7 @@
<script src="/static/js/tkuser-issue-types.js?v=2026031401"></script>
<script src="/static/js/tkuser-workplaces.js?v=2026031401"></script>
<script src="/static/js/tkuser-tasks.js?v=2026031401"></script>
<script src="/static/js/tkuser-vacations.js?v=2026032301"></script>
<script src="/static/js/tkuser-vacations.js?v=2026033101"></script>
<script src="/static/js/tkuser-vacation-settings.js?v=2026032501"></script>
<script src="/static/js/tkuser-layout-map.js?v=2026031401"></script>
<script src="/static/js/tkuser-partners.js?v=2026031601"></script>

View File

@@ -353,6 +353,7 @@ function openVacBalanceModal(editId) {
document.getElementById('vacBalModalTitle').textContent = '연차 배정';
const year = document.getElementById('vacYear')?.value || new Date().getFullYear();
document.getElementById('vbYear').value = year;
document.getElementById('vbExpiresAt').value = `${year}-12-31`;
// 사용자 셀렉트 (부서별 optgroup)
@@ -422,11 +423,10 @@ document.getElementById('vacBalanceForm').addEventListener('submit', async e =>
})});
showToast('수정되었습니다.');
} else {
const year = document.getElementById('vacYear')?.value || new Date().getFullYear();
await api('/vacations/balances', { method: 'POST', body: JSON.stringify({
user_id: parseInt(document.getElementById('vbUser').value),
vacation_type_id: parseInt(document.getElementById('vbType').value),
year: parseInt(year),
year: parseInt(document.getElementById('vbYear').value),
total_days: parseFloat(document.getElementById('vbTotalDays').value) || 0,
used_days: parseFloat(document.getElementById('vbUsedDays').value) || 0,
balance_type: document.getElementById('vbBalanceType').value,