const { getPool } = require('../middleware/auth'); const ALLOWED_CREATE_COLUMNS = ['user_id', 'vacation_type_id', 'start_date', 'end_date', 'days_used', 'reason', 'status', 'reviewed_by', 'review_note']; const ALLOWED_UPDATE_COLUMNS = ['vacation_type_id', 'start_date', 'end_date', 'days_used', 'reason']; const vacationRequestModel = { async create(data, conn) { const db = conn || getPool(); const filtered = {}; for (const key of ALLOWED_CREATE_COLUMNS) { if (data[key] !== undefined) filtered[key] = data[key]; } const columns = Object.keys(filtered); const placeholders = columns.map(() => '?').join(', '); const values = columns.map(c => filtered[c]); const [result] = await db.query( `INSERT INTO sp_vacation_requests (${columns.join(', ')}) VALUES (${placeholders})`, values ); return result; }, async getAll(filters = {}) { const db = getPool(); let query = ` SELECT vr.*, su.name as user_name, su.username, COALESCE(d.department_name, '미배정') as department_name, vt.type_name as vacation_type_name, vt.type_code, reviewer.name as reviewer_name FROM sp_vacation_requests vr INNER JOIN sso_users su ON vr.user_id = su.user_id LEFT JOIN departments d ON su.department_id = d.department_id INNER JOIN vacation_types vt ON vr.vacation_type_id = vt.id LEFT JOIN sso_users reviewer ON vr.reviewed_by = reviewer.user_id WHERE 1=1 `; const params = []; if (filters.user_id) { query += ' AND vr.user_id = ?'; params.push(filters.user_id); } if (filters.status) { query += ' AND vr.status = ?'; params.push(filters.status); } if (filters.start_date) { query += ' AND vr.start_date >= ?'; params.push(filters.start_date); } if (filters.end_date) { query += ' AND vr.end_date <= ?'; params.push(filters.end_date); } if (filters.vacation_type_id) { query += ' AND vr.vacation_type_id = ?'; params.push(filters.vacation_type_id); } if (filters.department_id) { query += ' AND su.department_id = ?'; params.push(filters.department_id); } query += ' ORDER BY vr.created_at DESC'; const [rows] = await db.query(query, params); return rows; }, async getById(requestId) { const db = getPool(); const [rows] = await db.query(` SELECT vr.*, su.name as user_name, su.username, COALESCE(d.department_name, '미배정') as department_name, vt.type_name as vacation_type_name, vt.type_code, reviewer.name as reviewer_name FROM sp_vacation_requests vr INNER JOIN sso_users su ON vr.user_id = su.user_id LEFT JOIN departments d ON su.department_id = d.department_id INNER JOIN vacation_types vt ON vr.vacation_type_id = vt.id LEFT JOIN sso_users reviewer ON vr.reviewed_by = reviewer.user_id WHERE vr.request_id = ? `, [requestId]); return rows; }, async update(requestId, data) { const db = getPool(); const filtered = {}; for (const key of ALLOWED_UPDATE_COLUMNS) { if (data[key] !== undefined) filtered[key] = data[key]; } const columns = Object.keys(filtered); if (columns.length === 0) return { affectedRows: 0 }; const setClause = columns.map(c => `${c} = ?`).join(', '); const values = [...columns.map(c => filtered[c]), requestId]; const [result] = await db.query(`UPDATE sp_vacation_requests SET ${setClause} WHERE request_id = ?`, values); return result; }, async updateStatus(requestId, statusData, conn) { const db = conn || getPool(); const [result] = await db.query(` UPDATE sp_vacation_requests SET status = ?, reviewed_by = ?, reviewed_at = NOW(), review_note = ? WHERE request_id = ? `, [statusData.status, statusData.reviewed_by, statusData.review_note || null, requestId]); return result; }, async checkOverlap(userId, startDate, endDate, excludeRequestId = null) { const db = getPool(); let query = ` SELECT COUNT(*) as count FROM sp_vacation_requests WHERE user_id = ? AND status IN ('pending', 'approved') AND start_date <= ? AND end_date >= ? `; const params = [userId, endDate, startDate]; if (excludeRequestId) { query += ' AND request_id != ?'; params.push(excludeRequestId); } const [rows] = await db.query(query, params); return rows; }, async getAllPending() { const db = getPool(); const [rows] = await db.query(` SELECT vr.*, su.name as user_name, su.username, COALESCE(d.department_name, '미배정') as department_name, vt.type_name as vacation_type_name, vt.type_code FROM sp_vacation_requests vr INNER JOIN sso_users su ON vr.user_id = su.user_id LEFT JOIN departments d ON su.department_id = d.department_id INNER JOIN vacation_types vt ON vr.vacation_type_id = vt.id WHERE vr.status = 'pending' ORDER BY vr.created_at ASC `); return rows; }, async runMigration() { const db = getPool(); const fs = require('fs'); const path = require('path'); const migrationFiles = ['001_create_sp_tables.sql', '002_section_c_additions.sql']; for (const file of migrationFiles) { const sqlFile = path.join(__dirname, '..', 'db', 'migrations', file); if (!fs.existsSync(sqlFile)) continue; const sql = fs.readFileSync(sqlFile, 'utf8'); const statements = sql.split(';').map(s => s.trim()).filter(s => s.length > 0); for (const stmt of statements) { try { await db.query(stmt); } catch (err) { if (err.code === 'ER_DUP_FIELDNAME' || err.code === 'ER_TABLE_EXISTS_ERROR') { // Already migrated } else { throw err; } } } } console.log('[tksupport] Migration completed'); } }; module.exports = vacationRequestModel;