refactor: worker_id → user_id 전체 마이그레이션 (Phase 1-4)
sso_users.user_id를 단일 식별자로 통합. JWT에서 worker_id 제거, department_id/is_production 추가. 백엔드 15개 모델, 11개 컨트롤러, 4개 서비스, 7개 라우트, 프론트엔드 32+ JS/11+ HTML 변환. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -271,7 +271,7 @@
|
||||
const select = document.getElementById('workerFilter');
|
||||
workers.forEach(worker => {
|
||||
const option = document.createElement('option');
|
||||
option.value = worker.worker_id;
|
||||
option.value = worker.user_id;
|
||||
option.textContent = worker.worker_name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
@@ -297,7 +297,7 @@
|
||||
params: {
|
||||
start_date: startDate,
|
||||
end_date: endDate,
|
||||
worker_id: workerId || undefined
|
||||
user_id: workerId || undefined
|
||||
}
|
||||
});
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
params: {
|
||||
start_date: startDate,
|
||||
end_date: endDate,
|
||||
worker_id: workerId || undefined
|
||||
user_id: workerId || undefined
|
||||
}
|
||||
});
|
||||
|
||||
@@ -340,10 +340,10 @@
|
||||
|
||||
// 출퇴근 기록 맵핑
|
||||
attendanceRecords.forEach(record => {
|
||||
const key = `${record.attendance_date}_${record.worker_id}`;
|
||||
const key = `${record.attendance_date}_${record.user_id}`;
|
||||
dateWorkerMap.set(key, {
|
||||
date: record.attendance_date,
|
||||
worker_id: record.worker_id,
|
||||
user_id: record.user_id,
|
||||
worker_name: record.worker_name,
|
||||
attendance: record,
|
||||
reports: []
|
||||
@@ -352,13 +352,13 @@
|
||||
|
||||
// 작업 보고서 맵핑
|
||||
workReports.forEach(report => {
|
||||
const key = `${report.report_date}_${report.worker_id}`;
|
||||
const key = `${report.report_date}_${report.user_id}`;
|
||||
if (dateWorkerMap.has(key)) {
|
||||
dateWorkerMap.get(key).reports.push(report);
|
||||
} else {
|
||||
dateWorkerMap.set(key, {
|
||||
date: report.report_date,
|
||||
worker_id: report.worker_id,
|
||||
user_id: report.user_id,
|
||||
worker_name: report.worker_name,
|
||||
attendance: null,
|
||||
reports: [report]
|
||||
|
||||
@@ -499,7 +499,7 @@
|
||||
function populateAssigneeDropdown() {
|
||||
const select = document.getElementById('receiveAssignee');
|
||||
select.innerHTML = '<option value="">담당자 선택</option>' +
|
||||
workers.map(w => `<option value="${w.worker_id}">${w.worker_name} (${w.job_type || ''})</option>`).join('');
|
||||
workers.map(w => `<option value="${w.user_id}">${w.worker_name} (${w.job_type || ''})</option>`).join('');
|
||||
}
|
||||
|
||||
async function loadRepairRequests() {
|
||||
|
||||
@@ -392,7 +392,7 @@
|
||||
// 데이터 정리
|
||||
vacationData = {};
|
||||
workers.forEach(w => {
|
||||
vacationData[w.worker_id] = {
|
||||
vacationData[w.user_id] = {
|
||||
carryover: 0,
|
||||
annual: 0,
|
||||
longService: 0,
|
||||
@@ -403,9 +403,9 @@
|
||||
|
||||
// 잔액 데이터 매핑
|
||||
balances.forEach(b => {
|
||||
if (!vacationData[b.worker_id]) return;
|
||||
if (!vacationData[b.user_id]) return;
|
||||
const code = b.type_code || '';
|
||||
const data = vacationData[b.worker_id];
|
||||
const data = vacationData[b.user_id];
|
||||
|
||||
if (code === 'CARRYOVER' || b.type_name === '이월') {
|
||||
data.carryover = b.total_days || 0;
|
||||
@@ -443,7 +443,7 @@
|
||||
}
|
||||
|
||||
tbody.innerHTML = workers.map((w, idx) => {
|
||||
const d = vacationData[w.worker_id] || { carryover: 0, annual: 0, longService: 0, specials: [], totalUsed: 0 };
|
||||
const d = vacationData[w.user_id] || { carryover: 0, annual: 0, longService: 0, specials: [], totalUsed: 0 };
|
||||
const carryover = parseFloat(d.carryover) || 0;
|
||||
const annual = parseFloat(d.annual) || 0;
|
||||
const longService = parseFloat(d.longService) || 0;
|
||||
@@ -454,29 +454,29 @@
|
||||
const remainingClass = remaining > 0 ? 'positive' : remaining < 0 ? 'negative' : 'zero';
|
||||
|
||||
return `
|
||||
<tr data-worker-id="${w.worker_id}">
|
||||
<tr data-user-id="${w.user_id}">
|
||||
<td>${idx + 1}</td>
|
||||
<td class="worker-name">${w.worker_name}</td>
|
||||
<td>
|
||||
<input type="number" class="num-input ${carryover < 0 ? 'negative' : ''}"
|
||||
value="${carryover}" step="0.5"
|
||||
data-field="carryover"
|
||||
onchange="updateField(${w.worker_id}, 'carryover', this.value)">
|
||||
onchange="updateField(${w.user_id}, 'carryover', this.value)">
|
||||
</td>
|
||||
<td>
|
||||
<input type="number" class="num-input"
|
||||
value="${annual}" step="0.5"
|
||||
data-field="annual"
|
||||
onchange="updateField(${w.worker_id}, 'annual', this.value)">
|
||||
onchange="updateField(${w.user_id}, 'annual', this.value)">
|
||||
</td>
|
||||
<td>
|
||||
<input type="number" class="num-input"
|
||||
value="${longService}" step="0.5"
|
||||
data-field="longService"
|
||||
onchange="updateField(${w.worker_id}, 'longService', this.value)">
|
||||
onchange="updateField(${w.user_id}, 'longService', this.value)">
|
||||
</td>
|
||||
<td>
|
||||
<button class="special-btn" onclick="openSpecialModal(${w.worker_id}, '${w.worker_name}')">
|
||||
<button class="special-btn" onclick="openSpecialModal(${w.user_id}, '${w.worker_name}')">
|
||||
${(d.specials || []).length > 0 ? `${specialTotal}일` : '추가'}
|
||||
${(d.specials || []).length > 0 ? `<span class="special-count">${d.specials.length}</span>` : ''}
|
||||
</button>
|
||||
@@ -497,7 +497,7 @@
|
||||
vacationData[workerId][field] = val;
|
||||
|
||||
// 입력 스타일 업데이트
|
||||
const input = document.querySelector(`tr[data-worker-id="${workerId}"] input[data-field="${field}"]`);
|
||||
const input = document.querySelector(`tr[data-user-id="${workerId}"] input[data-field="${field}"]`);
|
||||
if (input) {
|
||||
input.classList.toggle('negative', val < 0);
|
||||
}
|
||||
@@ -508,7 +508,7 @@
|
||||
}
|
||||
|
||||
function updateRowTotals(workerId) {
|
||||
const row = document.querySelector(`tr[data-worker-id="${workerId}"]`);
|
||||
const row = document.querySelector(`tr[data-user-id="${workerId}"]`);
|
||||
if (!row) return;
|
||||
|
||||
const d = vacationData[workerId];
|
||||
@@ -668,12 +668,12 @@
|
||||
|
||||
// 데이터 수집
|
||||
for (const w of workers) {
|
||||
const d = vacationData[w.worker_id];
|
||||
const d = vacationData[w.user_id];
|
||||
if (!d) continue;
|
||||
|
||||
if (typeIdMap['CARRYOVER']) {
|
||||
balancesToSave.push({
|
||||
worker_id: w.worker_id,
|
||||
user_id: w.user_id,
|
||||
vacation_type_id: typeIdMap['CARRYOVER'],
|
||||
year: currentYear,
|
||||
total_days: d.carryover
|
||||
@@ -681,7 +681,7 @@
|
||||
}
|
||||
if (typeIdMap['ANNUAL']) {
|
||||
balancesToSave.push({
|
||||
worker_id: w.worker_id,
|
||||
user_id: w.user_id,
|
||||
vacation_type_id: typeIdMap['ANNUAL'],
|
||||
year: currentYear,
|
||||
total_days: d.annual
|
||||
@@ -689,7 +689,7 @@
|
||||
}
|
||||
if (typeIdMap['LONG_SERVICE']) {
|
||||
balancesToSave.push({
|
||||
worker_id: w.worker_id,
|
||||
user_id: w.user_id,
|
||||
vacation_type_id: typeIdMap['LONG_SERVICE'],
|
||||
year: currentYear,
|
||||
total_days: d.longService
|
||||
@@ -726,7 +726,7 @@
|
||||
}
|
||||
if (specialTypeId) {
|
||||
balancesToSave.push({
|
||||
worker_id: w.worker_id,
|
||||
user_id: w.user_id,
|
||||
vacation_type_id: specialTypeId,
|
||||
year: currentYear,
|
||||
total_days: special.days,
|
||||
|
||||
@@ -270,18 +270,18 @@
|
||||
|
||||
checkinStatus = {};
|
||||
workers.forEach(w => {
|
||||
const checkin = checkinList.find(c => c.worker_id === w.worker_id);
|
||||
const record = records.find(r => r.worker_id === w.worker_id);
|
||||
const checkin = checkinList.find(c => c.user_id === w.user_id);
|
||||
const record = records.find(r => r.user_id === w.user_id);
|
||||
|
||||
if (checkin?.vacation_status === 'approved' || record?.vacation_type_id) {
|
||||
checkinStatus[w.worker_id] = { status: 'vacation', vacationType: checkin?.vacation_type_name || record?.vacation_type_name || '연차' };
|
||||
checkinStatus[w.user_id] = { status: 'vacation', vacationType: checkin?.vacation_type_name || record?.vacation_type_name || '연차' };
|
||||
} else if (record && record.is_present === 0) {
|
||||
checkinStatus[w.worker_id] = { status: 'absent' };
|
||||
checkinStatus[w.user_id] = { status: 'absent' };
|
||||
} else if (record && record.is_present === 1) {
|
||||
checkinStatus[w.worker_id] = { status: 'present' };
|
||||
checkinStatus[w.user_id] = { status: 'present' };
|
||||
} else {
|
||||
// 기록이 없으면 기본 출근
|
||||
checkinStatus[w.worker_id] = { status: 'present' };
|
||||
checkinStatus[w.user_id] = { status: 'present' };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -301,9 +301,9 @@
|
||||
}
|
||||
|
||||
container.innerHTML = workers.map(w => {
|
||||
const s = checkinStatus[w.worker_id] || { status: 'present' };
|
||||
const s = checkinStatus[w.user_id] || { status: 'present' };
|
||||
const label = s.status === 'present' ? '출근' : s.status === 'absent' ? '결근' : (s.vacationType || '연차');
|
||||
return `<span class="worker-chip ${s.status}" onclick="toggle(${w.worker_id})"><span class="chip-dot"></span>${w.worker_name} <small style="color:#6b7280">${label}</small></span>`;
|
||||
return `<span class="worker-chip ${s.status}" onclick="toggle(${w.user_id})"><span class="chip-dot"></span>${w.worker_name} <small style="color:#6b7280">${label}</small></span>`;
|
||||
}).join('');
|
||||
|
||||
updateSummary();
|
||||
@@ -318,8 +318,8 @@
|
||||
|
||||
function setAllPresent() {
|
||||
workers.forEach(w => {
|
||||
if (checkinStatus[w.worker_id]?.status !== 'vacation') {
|
||||
checkinStatus[w.worker_id] = { status: 'present' };
|
||||
if (checkinStatus[w.user_id]?.status !== 'vacation') {
|
||||
checkinStatus[w.user_id] = { status: 'present' };
|
||||
}
|
||||
});
|
||||
render();
|
||||
@@ -327,8 +327,8 @@
|
||||
|
||||
function setAllAbsent() {
|
||||
workers.forEach(w => {
|
||||
if (checkinStatus[w.worker_id]?.status !== 'vacation') {
|
||||
checkinStatus[w.worker_id] = { status: 'absent' };
|
||||
if (checkinStatus[w.user_id]?.status !== 'vacation') {
|
||||
checkinStatus[w.user_id] = { status: 'absent' };
|
||||
}
|
||||
});
|
||||
render();
|
||||
@@ -374,10 +374,10 @@
|
||||
|
||||
// 연차가 아닌 작업자들만 체크인 데이터로 전송
|
||||
const checkins = workers
|
||||
.filter(w => checkinStatus[w.worker_id]?.status !== 'vacation')
|
||||
.filter(w => checkinStatus[w.user_id]?.status !== 'vacation')
|
||||
.map(w => ({
|
||||
worker_id: w.worker_id,
|
||||
is_present: checkinStatus[w.worker_id]?.status === 'present'
|
||||
user_id: w.user_id,
|
||||
is_present: checkinStatus[w.user_id]?.status === 'present'
|
||||
}));
|
||||
|
||||
try {
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
|
||||
// 체크인 목록을 기준으로 출퇴근 기록 생성 (연차 정보 포함)
|
||||
attendanceRecords = checkinList.map(worker => {
|
||||
const existingRecord = existingRecords.find(r => r.worker_id === worker.worker_id);
|
||||
const existingRecord = existingRecords.find(r => r.user_id === worker.user_id);
|
||||
const isOnVacation = worker.vacation_status === 'approved';
|
||||
|
||||
// 기존 기록이 있으면 사용, 없으면 초기화
|
||||
@@ -185,7 +185,7 @@
|
||||
return existingRecord;
|
||||
} else {
|
||||
return {
|
||||
worker_id: worker.worker_id,
|
||||
user_id: worker.user_id,
|
||||
worker_name: worker.worker_name,
|
||||
attendance_date: selectedDate,
|
||||
total_hours: isOnVacation ? 0 : 8,
|
||||
@@ -209,7 +209,7 @@
|
||||
function initializeAttendanceRecords() {
|
||||
const selectedDate = document.getElementById('selectedDate').value;
|
||||
attendanceRecords = workers.map(worker => ({
|
||||
worker_id: worker.worker_id,
|
||||
user_id: worker.user_id,
|
||||
worker_name: worker.worker_name,
|
||||
attendance_date: selectedDate,
|
||||
total_hours: 8,
|
||||
@@ -351,7 +351,7 @@
|
||||
|
||||
// 모든 기록을 API 형식에 맞게 변환
|
||||
const recordsToSave = attendanceRecords.map(record => ({
|
||||
worker_id: record.worker_id,
|
||||
user_id: record.user_id,
|
||||
attendance_date: selectedDate,
|
||||
total_hours: record.total_hours || 0,
|
||||
overtime_hours: record.overtime_hours || 0,
|
||||
@@ -371,7 +371,7 @@
|
||||
successCount++;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`작업자 ${data.worker_id} 저장 오류:`, error);
|
||||
console.error(`작업자 ${data.user_id} 저장 오류:`, error);
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -662,7 +662,7 @@
|
||||
const records = response.data.data;
|
||||
// 작업자별로 데이터 정리
|
||||
records.forEach(record => {
|
||||
const workerId = record.worker_id;
|
||||
const workerId = record.user_id;
|
||||
// 날짜 형식 정규화 (다양한 형식 처리)
|
||||
let dateKey = record.record_date;
|
||||
if (dateKey) {
|
||||
@@ -686,7 +686,7 @@
|
||||
console.error('근태 데이터 조회 오류:', err);
|
||||
// 작업자별 빈 데이터 초기화
|
||||
workers.forEach(worker => {
|
||||
attendanceData[worker.worker_id] = {};
|
||||
attendanceData[worker.user_id] = {};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -765,7 +765,7 @@
|
||||
html += `<tbody>`;
|
||||
|
||||
workers.forEach((worker, index) => {
|
||||
const workerRecords = attendanceData[worker.worker_id] || {};
|
||||
const workerRecords = attendanceData[worker.user_id] || {};
|
||||
let totalOvertimeHours = 0;
|
||||
|
||||
// 입사일 파싱
|
||||
@@ -954,7 +954,7 @@
|
||||
let totalNormalDays = 0;
|
||||
|
||||
workers.forEach(worker => {
|
||||
const records = attendanceData[worker.worker_id] || {};
|
||||
const records = attendanceData[worker.user_id] || {};
|
||||
Object.values(records).forEach(record => {
|
||||
const hours = parseFloat(record.total_work_hours) || 0;
|
||||
totalWorkHours += hours;
|
||||
|
||||
@@ -306,13 +306,13 @@
|
||||
document.getElementById('adminControls').classList.add('visible');
|
||||
await loadWorkers();
|
||||
} else {
|
||||
// 일반 사용자: 본인 worker_id 사용
|
||||
if (currentUser?.worker_id) {
|
||||
currentWorkerId = currentUser.worker_id;
|
||||
// 일반 사용자: 본인 user_id 사용
|
||||
if (currentUser?.user_id) {
|
||||
currentWorkerId = currentUser.user_id;
|
||||
document.getElementById('infoGrid').style.display = 'grid';
|
||||
await loadAllData();
|
||||
} else {
|
||||
// worker_id가 없는 경우
|
||||
// user_id가 없는 경우
|
||||
document.getElementById('noWorkerMessage').style.display = 'block';
|
||||
}
|
||||
}
|
||||
@@ -352,7 +352,7 @@
|
||||
const select = document.getElementById('workerSelect');
|
||||
workers.forEach(w => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = w.worker_id;
|
||||
opt.value = w.user_id;
|
||||
opt.textContent = w.worker_name;
|
||||
select.appendChild(opt);
|
||||
});
|
||||
@@ -369,7 +369,7 @@
|
||||
}
|
||||
|
||||
currentWorkerId = parseInt(workerId);
|
||||
const worker = workers.find(w => w.worker_id === currentWorkerId);
|
||||
const worker = workers.find(w => w.user_id === currentWorkerId);
|
||||
document.getElementById('workerNameDisplay').textContent =
|
||||
`${worker?.worker_name || ''}님의 연차 잔여 현황 및 월간 연장근로 시간`;
|
||||
document.getElementById('infoGrid').style.display = 'grid';
|
||||
@@ -486,7 +486,7 @@
|
||||
|
||||
try {
|
||||
// 근태 기록에서 연장근로 데이터 조회
|
||||
const res = await axios.get(`/attendance/records?start_date=${startDate}&end_date=${endDate}&worker_id=${currentWorkerId}`);
|
||||
const res = await axios.get(`/attendance/records?start_date=${startDate}&end_date=${endDate}&user_id=${currentWorkerId}`);
|
||||
const records = res.data.data || [];
|
||||
|
||||
// 8시간 초과분 계산
|
||||
|
||||
@@ -222,7 +222,7 @@
|
||||
event.preventDefault();
|
||||
|
||||
const data = {
|
||||
worker_id: parseInt(document.getElementById('inputWorker').value),
|
||||
user_id: parseInt(document.getElementById('inputWorker').value),
|
||||
vacation_type_id: parseInt(document.getElementById('inputVacationType').value),
|
||||
start_date: document.getElementById('inputStartDate').value,
|
||||
end_date: document.getElementById('inputEndDate').value,
|
||||
|
||||
@@ -369,7 +369,7 @@
|
||||
event.preventDefault();
|
||||
|
||||
const data = {
|
||||
worker_id: parseInt(document.getElementById('inputWorker').value),
|
||||
user_id: parseInt(document.getElementById('inputWorker').value),
|
||||
vacation_type_id: parseInt(document.getElementById('inputVacationType').value),
|
||||
start_date: document.getElementById('inputStartDate').value,
|
||||
end_date: document.getElementById('inputEndDate').value,
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
try {
|
||||
const currentUser = getCurrentUser();
|
||||
|
||||
if (!currentUser || !currentUser.worker_id) {
|
||||
if (!currentUser || !currentUser.user_id) {
|
||||
alert('작업자 정보가 없습니다. 관리자에게 문의하세요.');
|
||||
return;
|
||||
}
|
||||
@@ -179,7 +179,7 @@
|
||||
const currentUser = getCurrentUser();
|
||||
|
||||
try {
|
||||
const response = await axios.get(`/attendance/vacation-balance/${currentUser.worker_id}`);
|
||||
const response = await axios.get(`/attendance/vacation-balance/${currentUser.user_id}`);
|
||||
if (response.data.success) {
|
||||
renderVacationBalance(response.data.data);
|
||||
}
|
||||
@@ -222,7 +222,7 @@
|
||||
|
||||
const currentUser = getCurrentUser();
|
||||
const data = {
|
||||
worker_id: currentUser.worker_id,
|
||||
user_id: currentUser.user_id,
|
||||
vacation_type_id: parseInt(document.getElementById('vacationType').value),
|
||||
start_date: document.getElementById('startDate').value,
|
||||
end_date: document.getElementById('endDate').value,
|
||||
@@ -251,7 +251,7 @@
|
||||
if (response.data.success) {
|
||||
// 내 신청만 필터링
|
||||
const myRequests = response.data.data.filter(req =>
|
||||
req.requested_by === currentUser.user_id || req.worker_id === currentUser.worker_id
|
||||
req.requested_by === currentUser.user_id || req.user_id === currentUser.user_id
|
||||
);
|
||||
renderVacationRequests(myRequests, 'myRequestsList', true, 'delete');
|
||||
}
|
||||
|
||||
@@ -329,7 +329,7 @@
|
||||
|
||||
workStatus = {};
|
||||
workers.forEach(w => {
|
||||
const record = records.find(r => r.worker_id === w.worker_id);
|
||||
const record = records.find(r => r.user_id === w.user_id);
|
||||
|
||||
// 입사일 이전인지 확인
|
||||
const joinDate = w.join_date ? w.join_date.split('T')[0] : null;
|
||||
@@ -337,7 +337,7 @@
|
||||
|
||||
if (isBeforeJoin) {
|
||||
// 입사 전 날짜
|
||||
workStatus[w.worker_id] = {
|
||||
workStatus[w.user_id] = {
|
||||
isPresent: false,
|
||||
type: 'not_hired',
|
||||
hours: 0,
|
||||
@@ -396,7 +396,7 @@
|
||||
|
||||
const typeInfo = attendanceTypes.find(t => t.value === type);
|
||||
|
||||
workStatus[w.worker_id] = {
|
||||
workStatus[w.user_id] = {
|
||||
isPresent: record.is_present === 1 || typeInfo?.isLeave,
|
||||
type: type,
|
||||
hours: typeInfo !== undefined ? typeInfo.hours : 8,
|
||||
@@ -406,7 +406,7 @@
|
||||
};
|
||||
} else {
|
||||
// 출근 체크 기록이 없는 경우 - 결근 상태
|
||||
workStatus[w.worker_id] = {
|
||||
workStatus[w.user_id] = {
|
||||
isPresent: false,
|
||||
type: 'normal',
|
||||
hours: 8,
|
||||
@@ -435,7 +435,7 @@
|
||||
}
|
||||
|
||||
tbody.innerHTML = workers.map((w, idx) => {
|
||||
const s = workStatus[w.worker_id];
|
||||
const s = workStatus[w.user_id];
|
||||
|
||||
// 미입사 상태 처리
|
||||
if (s.isNotHired) {
|
||||
@@ -503,7 +503,7 @@
|
||||
${statusText}
|
||||
</td>
|
||||
<td>
|
||||
<select class="type-select" onchange="updateType(${w.worker_id}, this.value)">
|
||||
<select class="type-select" onchange="updateType(${w.user_id}, this.value)">
|
||||
${attendanceTypes.map(t => `
|
||||
<option value="${t.value}" ${s.type === t.value ? 'selected' : ''}>${t.label}</option>
|
||||
`).join('')}
|
||||
@@ -513,7 +513,7 @@
|
||||
<td class="hours-cell">
|
||||
${showOvertimeInput ? `
|
||||
<input type="number" class="overtime-input" value="${s.overtimeHours}" min="0" max="8" step="0.5"
|
||||
onchange="updateOvertime(${w.worker_id}, this.value)">
|
||||
onchange="updateOvertime(${w.user_id}, this.value)">
|
||||
` : '-'}
|
||||
</td>
|
||||
<td class="hours-cell"><strong>${totalHours}h</strong></td>
|
||||
@@ -551,9 +551,9 @@
|
||||
|
||||
function setAllNormal() {
|
||||
workers.forEach(w => {
|
||||
workStatus[w.worker_id].type = 'normal';
|
||||
workStatus[w.worker_id].hours = 8;
|
||||
workStatus[w.worker_id].overtimeHours = 0;
|
||||
workStatus[w.user_id].type = 'normal';
|
||||
workStatus[w.user_id].hours = 8;
|
||||
workStatus[w.user_id].overtimeHours = 0;
|
||||
});
|
||||
render();
|
||||
updateSummary();
|
||||
@@ -638,14 +638,14 @@
|
||||
|
||||
// 미입사자 제외하고 저장할 데이터 생성
|
||||
const recordsToSave = workers
|
||||
.filter(w => !workStatus[w.worker_id]?.isNotHired)
|
||||
.filter(w => !workStatus[w.user_id]?.isNotHired)
|
||||
.map(w => {
|
||||
const s = workStatus[w.worker_id];
|
||||
const s = workStatus[w.user_id];
|
||||
const totalHours = s.type === 'overtime' ? s.hours + s.overtimeHours : s.hours;
|
||||
|
||||
return {
|
||||
record_date: date,
|
||||
worker_id: w.worker_id,
|
||||
user_id: w.user_id,
|
||||
attendance_type_id: typeIdMap[s.type] || 1,
|
||||
vacation_type_id: vacationTypeIdMap[s.type] || null,
|
||||
total_work_hours: totalHours,
|
||||
@@ -674,8 +674,8 @@
|
||||
alert(`${ok}명 저장 완료`);
|
||||
isAlreadySaved = true;
|
||||
workers.forEach(w => {
|
||||
if (workStatus[w.worker_id]) {
|
||||
workStatus[w.worker_id].isSaved = true;
|
||||
if (workStatus[w.user_id]) {
|
||||
workStatus[w.user_id].isSaved = true;
|
||||
}
|
||||
});
|
||||
render();
|
||||
|
||||
Reference in New Issue
Block a user