refactor: 작업자 관리 개선 - 계정 연동 기능으로 변경

작업 보고서 표시 여부 대신 계정 연동 기능으로 개선했습니다.

## 주요 변경사항

### 개념 변경
- **이전**: 작업 보고서 표시 여부 (show_in_work_reports)
- **이후**: 계정 생성/연동 기능

### 데이터베이스
- **마이그레이션**: 20260119095549_add_worker_display_fields.js
  - show_in_work_reports 컬럼 제거
  - employment_status만 유지 (employed/resigned)

- **workerModel**:
  - getAll, getById에서 users 테이블 JOIN하여 user_id 조회
  - create, update에서 show_in_work_reports 필드 제거

### 백엔드 API
- **workerController.js**:
  - createWorker: create_account 체크 시 자동으로 users 테이블에 계정 생성
    - username: hangulToRoman으로 한글 이름 변환
    - password: 초기 비밀번호 '1234' (bcrypt 해시)
    - role: User 역할 자동 할당
  - updateWorker:
    - create_account=true & 계정 없음 → 계정 생성
    - create_account=false & 계정 있음 → 계정 연동 해제 (users.worker_id=NULL)

### 프론트엔드
- **worker-management.html**:
  - "작업 보고서 표시" → "🔐 계정 생성/연동"으로 변경
  - 체크 시 로그인 계정 자동 생성 안내

- **worker-management.js**:
  - 카드 렌더링: user_id 존재 여부로 계정 연동 상태 표시 (🔐 아이콘)
  - saveWorker: create_account 필드 전송
  - show_in_work_reports 관련 로직 모두 제거

- **daily-work-report.js**:
  - 필터링 조건 단순화: 퇴사자만 제외 (employment_status≠resigned)
  - 계정 여부와 무관하게 모든 재직자 표시

## 사용 방법
1. 작업자 등록/수정 시 "계정 생성/연동" 체크
2. 자동으로 로그인 계정 생성 (초기 비밀번호: 1234)
3. 계정이 있는 작업자는 나의 대시보드, 연차/출퇴근 관리 가능

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-01-19 10:17:31 +09:00
parent 25cca1482e
commit bea62dfdee
6 changed files with 120 additions and 55 deletions

View File

@@ -211,16 +211,14 @@ async function loadWorkers() {
const allWorkers = Array.isArray(data) ? data : (data.data || data.workers || []);
// 작업 보고서에 표시할 작업자만 필터링
// 1. show_in_work_reports가 true
// 2. employment_status가 resigned가 아님
// 퇴사자만 제외 (계정 여부와 무관하게 재직자는 모두 표시)
workers = allWorkers.filter(worker => {
const showInReports = worker.show_in_work_reports !== 0 && worker.show_in_work_reports !== false;
const notResigned = worker.employment_status !== 'resigned';
return showInReports && notResigned;
return notResigned;
});
console.log(`✅ Workers 로드 성공: ${workers.length}명 (전체: ${allWorkers.length}명)`);
console.log(`📊 필터링 조건: show_in_work_reports=true, employment_status≠resigned`);
console.log(`📊 필터링 조건: employment_status≠resigned (퇴사자만 제외)`);
} catch (error) {
console.error('작업자 로딩 오류:', error);
throw error;

View File

@@ -198,7 +198,7 @@ function renderWorkers() {
const jobType = jobTypeMap[worker.job_type] || jobTypeMap['worker'];
const isInactive = worker.status === 'inactive' || worker.is_active === 0 || worker.is_active === false;
const isResigned = worker.employment_status === 'resigned';
const showInWorkReports = worker.show_in_work_reports !== 0 && worker.show_in_work_reports !== false;
const hasAccount = worker.user_id !== null && worker.user_id !== undefined;
console.log('🎨 카드 렌더링:', {
worker_id: worker.worker_id,
@@ -207,7 +207,8 @@ function renderWorkers() {
is_active: worker.is_active,
isInactive: isInactive,
isResigned: isResigned,
showInWorkReports: showInWorkReports
user_id: worker.user_id,
hasAccount: hasAccount
});
return `
@@ -221,17 +222,18 @@ function renderWorkers() {
</div>
<h3 class="project-name">
${worker.worker_name}
${hasAccount ? '<span style="color: #10b981; font-size: 0.8rem; margin-left: 0.5rem;">🔐</span>' : ''}
${isResigned ? '<span class="inactive-label" style="color: #dc2626;">(퇴사)</span>' :
isInactive ? '<span class="inactive-label">(사무직)</span>' : ''}
</h3>
<div class="project-meta">
<span style="color: ${jobType.color}; font-weight: 500;">${jobType.icon} ${jobType.text}</span>
${hasAccount ? '<span style="color: #10b981;">🔐 계정 연동됨</span>' : '<span style="color: #9ca3af;">⚪ 계정 없음</span>'}
${worker.phone_number ? `<span>📞 ${worker.phone_number}</span>` : ''}
${worker.email ? `<span>📧 ${worker.email}</span>` : ''}
${worker.department ? `<span>🏢 ${worker.department}</span>` : ''}
${worker.hire_date ? `<span>📅 입사: ${formatDate(worker.hire_date)}</span>` : ''}
${isResigned ? '<span class="inactive-notice" style="color: #dc2626;">⚠️ 퇴사 처리됨</span>' :
!showInWorkReports ? '<span class="inactive-notice">⚠️ 작업보고서에서 숨김</span>' : ''}
${isResigned ? '<span class="inactive-notice" style="color: #dc2626;">⚠️ 퇴사 처리됨</span>' : ''}
</div>
</div>
<div class="project-actions">
@@ -456,9 +458,9 @@ function openWorkerModal(worker = null) {
const isActiveValue = worker.status !== 'inactive' && worker.is_active !== 0 && worker.is_active !== false;
document.getElementById('isActive').checked = isActiveValue;
// show_in_work_reports 값 처리
const showInWorkReportsValue = worker.show_in_work_reports !== 0 && worker.show_in_work_reports !== false;
document.getElementById('showInWorkReports').checked = showInWorkReportsValue;
// 계정 연동 여부 확인 (user_id가 있으면 계정 있음)
const hasAccountValue = worker.user_id !== null && worker.user_id !== undefined;
document.getElementById('hasAccount').checked = hasAccountValue;
// employment_status 값 처리 (resigned이면 체크)
const isResignedValue = worker.employment_status === 'resigned';
@@ -471,7 +473,8 @@ function openWorkerModal(worker = null) {
status: worker.status,
is_active_raw: worker.is_active,
is_active_processed: isActiveValue,
show_in_work_reports: showInWorkReportsValue,
user_id: worker.user_id,
has_account: hasAccountValue,
employment_status: worker.employment_status,
is_resigned: isResignedValue
});
@@ -484,7 +487,7 @@ function openWorkerModal(worker = null) {
document.getElementById('workerForm').reset();
document.getElementById('workerId').value = '';
document.getElementById('isActive').checked = true;
document.getElementById('showInWorkReports').checked = true;
document.getElementById('hasAccount').checked = false;
document.getElementById('isResigned').checked = false;
}
@@ -531,8 +534,8 @@ async function saveWorker() {
salary: document.getElementById('salary')?.value || null,
annual_leave: document.getElementById('annualLeave')?.value || null,
status: document.getElementById('isActive').checked ? 'active' : 'inactive',
show_in_work_reports: document.getElementById('showInWorkReports').checked,
employment_status: document.getElementById('isResigned').checked ? 'resigned' : 'employed'
employment_status: document.getElementById('isResigned').checked ? 'resigned' : 'employed',
create_account: document.getElementById('hasAccount').checked // 계정 생성 여부
};
console.log('💾 저장할 작업자 데이터:', JSON.stringify(workerData, null, 2));
@@ -597,7 +600,6 @@ async function toggleWorkerStatus(workerId) {
join_date: worker.join_date || null,
salary: worker.salary || null,
annual_leave: worker.annual_leave || null,
show_in_work_reports: worker.show_in_work_reports !== 0 && worker.show_in_work_reports !== false,
employment_status: worker.employment_status || 'employed'
};

View File

@@ -176,19 +176,19 @@
<label class="form-label" style="font-weight: 600; margin-bottom: 0.75rem; display: block;">상태 관리</label>
<div style="display: flex; flex-direction: column; gap: 0.75rem;">
<!-- 작업 보고서 표시 -->
<!-- 계정 생성/연동 -->
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" id="showInWorkReports" checked style="margin: 0; cursor: pointer;">
<span>작업 보고서에 표시</span>
<input type="checkbox" id="hasAccount" style="margin: 0; cursor: pointer;">
<span>🔐 계정 생성/연동</span>
</label>
<small style="color: #6b7280; font-size: 0.75rem; margin-top: -0.5rem; margin-left: 1.5rem;">
체크 해제 시 일일 작업보고서 작성 시 이 작업자가 목록에 나타나지 않습니다
체크 시 로그인 계정이 자동 생성됩니다 (나의 대시보드, 연차/출퇴근 관리 가능)
</small>
<!-- 현장직/사무직 구분 -->
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" id="isActive" checked style="margin: 0; cursor: pointer;">
<span>현장직 (활성화)</span>
<span>🏭 현장직 (활성화)</span>
</label>
<small style="color: #6b7280; font-size: 0.75rem; margin-top: -0.5rem; margin-left: 1.5rem;">
체크: 현장직 (출퇴근 관리 필요) / 체크 해제: 사무직 (출퇴근 관리 불필요)
@@ -197,10 +197,10 @@
<!-- 퇴사 처리 -->
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" id="isResigned" style="margin: 0; cursor: pointer;">
<span style="color: #ef4444;">퇴사 처리</span>
<span style="color: #ef4444;">🚪 퇴사 처리</span>
</label>
<small style="color: #ef4444; font-size: 0.75rem; margin-top: -0.5rem; margin-left: 1.5rem;">
퇴사한 작업자로 표시됩니다. 작업자 목록에서 별도로 표시됩니다
퇴사한 작업자로 표시됩니다. 작업 보고서에서 제외됩니다
</small>
</div>
</div>