- 월별 캘린더 UI로 작업 현황을 한눈에 확인 가능 - 미입력(빨강), 부분입력(주황), 확인필요(보라), 이상없음(초록) 상태 표시 - 범례 아이콘(●)을 사용한 직관적인 상태 표시 - 날짜 클릭 시 해당일 작업자별 상세 현황 모달 - 작업자 클릭 시 개별 작업 입력/수정 모달 - 휴가 처리 기능 (연차, 반차, 반반차, 조퇴) - 월별 집계 데이터 최적화로 API 호출 최소화 백엔드: - monthly_worker_status, monthly_summary 테이블 추가 - 자동 집계 stored procedure 및 trigger 구현 - 확인필요(12시간 초과) 상태 감지 로직 - 출석 관리 시스템 확장 프론트엔드: - 캘린더 그리드 UI 구현 - 상태별 색상 및 아이콘 표시 - 모달 기반 상세 정보 표시 - 반응형 디자인 적용
194 lines
8.3 KiB
JavaScript
194 lines
8.3 KiB
JavaScript
// 근태 관리 테이블 생성 스크립트
|
|
const mysql = require('mysql2/promise');
|
|
|
|
async function createAttendanceTables() {
|
|
let connection;
|
|
|
|
try {
|
|
// 로컬 MySQL 연결 (기본 설정)
|
|
connection = await mysql.createConnection({
|
|
host: 'localhost',
|
|
user: 'root',
|
|
password: '', // 비밀번호가 있다면 여기에 입력
|
|
database: 'hyungi'
|
|
});
|
|
|
|
console.log('✅ MySQL 연결 성공');
|
|
|
|
// 1. 근로 유형 테이블 생성
|
|
console.log('📋 근로 유형 테이블 생성 중...');
|
|
await connection.execute(`
|
|
CREATE TABLE IF NOT EXISTS work_attendance_types (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
type_code VARCHAR(20) NOT NULL UNIQUE COMMENT '근로 유형 코드',
|
|
type_name VARCHAR(50) NOT NULL COMMENT '근로 유형명',
|
|
description TEXT COMMENT '설명',
|
|
is_active BOOLEAN DEFAULT TRUE COMMENT '활성 상태',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
) COMMENT='근로 유형 관리 테이블'
|
|
`);
|
|
|
|
// 2. 휴가 유형 테이블 생성
|
|
console.log('🏖️ 휴가 유형 테이블 생성 중...');
|
|
await connection.execute(`
|
|
CREATE TABLE IF NOT EXISTS vacation_types (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
type_code VARCHAR(20) NOT NULL UNIQUE COMMENT '휴가 유형 코드',
|
|
type_name VARCHAR(50) NOT NULL COMMENT '휴가 유형명',
|
|
hours_deduction DECIMAL(4,2) NOT NULL COMMENT '차감 시간',
|
|
description TEXT COMMENT '설명',
|
|
is_active BOOLEAN DEFAULT TRUE COMMENT '활성 상태',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
) COMMENT='휴가 유형 관리 테이블'
|
|
`);
|
|
|
|
// 3. 일일 근태 기록 테이블 생성
|
|
console.log('📊 일일 근태 기록 테이블 생성 중...');
|
|
await connection.execute(`
|
|
CREATE TABLE IF NOT EXISTS daily_attendance_records (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
record_date DATE NOT NULL COMMENT '기록 날짜',
|
|
worker_id INT NOT NULL COMMENT '작업자 ID',
|
|
total_work_hours DECIMAL(4,2) DEFAULT 0 COMMENT '총 작업 시간',
|
|
attendance_type_id INT COMMENT '근로 유형 ID',
|
|
vacation_type_id INT NULL COMMENT '휴가 유형 ID',
|
|
is_vacation_processed BOOLEAN DEFAULT FALSE COMMENT '휴가 처리 여부',
|
|
overtime_approved BOOLEAN DEFAULT FALSE COMMENT '초과근무 승인 여부',
|
|
overtime_approved_by INT NULL COMMENT '초과근무 승인자 ID',
|
|
overtime_approved_at TIMESTAMP NULL COMMENT '초과근무 승인 시간',
|
|
status ENUM('incomplete', 'partial', 'complete', 'overtime', 'vacation', 'error') DEFAULT 'incomplete' COMMENT '상태',
|
|
notes TEXT COMMENT '비고',
|
|
created_by INT NOT NULL DEFAULT 1 COMMENT '생성자 ID',
|
|
updated_by INT NULL COMMENT '수정자 ID',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
UNIQUE KEY unique_worker_date (worker_id, record_date),
|
|
INDEX idx_record_date (record_date),
|
|
INDEX idx_worker_date (worker_id, record_date),
|
|
INDEX idx_status (status)
|
|
) COMMENT='일일 근태 기록 테이블'
|
|
`);
|
|
|
|
// 4. 작업자 휴가 잔여 관리 테이블 생성
|
|
console.log('📅 휴가 잔여 관리 테이블 생성 중...');
|
|
await connection.execute(`
|
|
CREATE TABLE IF NOT EXISTS worker_vacation_balance (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
worker_id INT NOT NULL COMMENT '작업자 ID',
|
|
year YEAR NOT NULL COMMENT '연도',
|
|
total_annual_leave DECIMAL(4,2) DEFAULT 15.0 COMMENT '연간 총 연차 (일)',
|
|
used_annual_leave DECIMAL(4,2) DEFAULT 0 COMMENT '사용한 연차 (일)',
|
|
remaining_annual_leave DECIMAL(4,2) GENERATED ALWAYS AS (total_annual_leave - used_annual_leave) STORED COMMENT '잔여 연차 (일)',
|
|
notes TEXT COMMENT '비고',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
UNIQUE KEY unique_worker_year (worker_id, year),
|
|
INDEX idx_worker_year (worker_id, year)
|
|
) COMMENT='작업자별 휴가 잔여 관리 테이블'
|
|
`);
|
|
|
|
// 5. 기본 데이터 삽입
|
|
console.log('📝 기본 데이터 삽입 중...');
|
|
|
|
// 근로 유형 기본 데이터
|
|
await connection.execute(`
|
|
INSERT IGNORE INTO work_attendance_types (type_code, type_name, description) VALUES
|
|
('REGULAR', '정시근로', '8시간 정규 근무'),
|
|
('OVERTIME', '연장근로', '8시간 초과 근무'),
|
|
('PARTIAL', '부분근로', '8시간 미만 근무'),
|
|
('VACATION', '휴가근로', '휴가와 함께하는 부분 근무')
|
|
`);
|
|
|
|
// 휴가 유형 기본 데이터
|
|
await connection.execute(`
|
|
INSERT IGNORE INTO vacation_types (type_code, type_name, hours_deduction, description) VALUES
|
|
('ANNUAL_FULL', '연차', 8.0, '하루 전체 연차'),
|
|
('ANNUAL_HALF', '반차', 4.0, '반일 연차'),
|
|
('ANNUAL_QUARTER', '반반차', 2.0, '1/4일 연차'),
|
|
('SICK_FULL', '병가', 8.0, '하루 전체 병가'),
|
|
('SICK_HALF', '반일병가', 4.0, '반일 병가'),
|
|
('PERSONAL_FULL', '개인사유', 8.0, '개인사유로 인한 휴가'),
|
|
('PERSONAL_HALF', '반일개인사유', 4.0, '반일 개인사유 휴가')
|
|
`);
|
|
|
|
// 6. 휴가 전용 작업 유형 추가
|
|
console.log('🏖️ 휴가 전용 작업 유형 추가 중...');
|
|
await connection.execute(`
|
|
INSERT IGNORE INTO work_types (name, description, is_active) VALUES
|
|
('휴가', '연차, 반차, 병가 등 휴가 처리용', TRUE)
|
|
`);
|
|
|
|
// 7. daily_work_reports 테이블에 근태 기록 연결 컬럼 추가 (이미 있으면 무시)
|
|
try {
|
|
await connection.execute(`
|
|
ALTER TABLE daily_work_reports
|
|
ADD COLUMN attendance_record_id INT NULL COMMENT '근태 기록 ID' AFTER updated_by
|
|
`);
|
|
console.log('✅ daily_work_reports 테이블에 attendance_record_id 컬럼 추가됨');
|
|
} catch (error) {
|
|
if (error.code !== 'ER_DUP_FIELDNAME') {
|
|
console.log('⚠️ attendance_record_id 컬럼 추가 실패:', error.message);
|
|
} else {
|
|
console.log('✅ attendance_record_id 컬럼이 이미 존재함');
|
|
}
|
|
}
|
|
|
|
// 8. 인덱스 추가
|
|
try {
|
|
await connection.execute(`CREATE INDEX idx_attendance_record ON daily_work_reports(attendance_record_id)`);
|
|
console.log('✅ attendance_record_id 인덱스 추가됨');
|
|
} catch (error) {
|
|
console.log('⚠️ 인덱스 추가 실패 (이미 존재할 수 있음):', error.message);
|
|
}
|
|
|
|
console.log('🎉 근태 관리 DB 설정 완료!');
|
|
console.log('');
|
|
console.log('📋 생성된 테이블:');
|
|
console.log(' - work_attendance_types (근로 유형)');
|
|
console.log(' - vacation_types (휴가 유형)');
|
|
console.log(' - daily_attendance_records (일일 근태 기록)');
|
|
console.log(' - worker_vacation_balance (휴가 잔여 관리)');
|
|
console.log('');
|
|
console.log('✅ 기본 데이터도 모두 삽입되었습니다.');
|
|
|
|
} catch (error) {
|
|
console.error('❌ DB 설정 중 오류 발생:', error);
|
|
|
|
// 다른 연결 정보로 시도
|
|
if (error.code === 'ECONNREFUSED' || error.code === 'ER_ACCESS_DENIED_ERROR') {
|
|
console.log('');
|
|
console.log('💡 다른 DB 연결 정보를 시도해보세요:');
|
|
console.log(' - host: localhost 또는 127.0.0.1');
|
|
console.log(' - port: 3306 (기본값)');
|
|
console.log(' - user: root 또는 다른 사용자');
|
|
console.log(' - password: 설정된 비밀번호');
|
|
console.log(' - database: hyungi');
|
|
}
|
|
|
|
throw error;
|
|
} finally {
|
|
if (connection) {
|
|
await connection.end();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 직접 실행
|
|
if (require.main === module) {
|
|
createAttendanceTables()
|
|
.then(() => {
|
|
console.log('✅ 설정 완료');
|
|
process.exit(0);
|
|
})
|
|
.catch((error) => {
|
|
console.error('❌ 설정 실패:', error);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
module.exports = { createAttendanceTables };
|