const { getPool } = require('./partnerModel'); async function findAll({ company_id, date_from, date_to, schedule_id, confirmed, page = 1, limit = 50 } = {}) { const db = getPool(); let sql = `SELECT wr.*, pc.company_name, ps.work_description AS schedule_description, su_reporter.name AS reporter_name, su_confirmer.name AS confirmed_by_name FROM partner_work_reports wr LEFT JOIN partner_companies pc ON wr.company_id = pc.id LEFT JOIN partner_schedules ps ON wr.schedule_id = ps.id LEFT JOIN sso_users su_reporter ON wr.reporter_id = su_reporter.user_id LEFT JOIN sso_users su_confirmer ON wr.confirmed_by = su_confirmer.user_id WHERE 1=1`; const params = []; if (company_id) { sql += ' AND wr.company_id = ?'; params.push(company_id); } if (date_from) { sql += ' AND wr.report_date >= ?'; params.push(date_from); } if (date_to) { sql += ' AND wr.report_date <= ?'; params.push(date_to); } if (schedule_id) { sql += ' AND wr.schedule_id = ?'; params.push(schedule_id); } if (confirmed === 'true' || confirmed === '1') { sql += ' AND wr.confirmed_by IS NOT NULL'; } if (confirmed === 'false' || confirmed === '0') { sql += ' AND wr.confirmed_by IS NULL'; } sql += ' ORDER BY wr.report_date DESC, wr.report_seq ASC, wr.created_at DESC'; const offset = (page - 1) * limit; sql += ' LIMIT ? OFFSET ?'; params.push(limit, offset); const [rows] = await db.query(sql, params); return rows; } async function findById(id) { const db = getPool(); const [rows] = await db.query( `SELECT wr.*, pc.company_name, ps.work_description AS schedule_description, su_reporter.name AS reporter_name, su_confirmer.name AS confirmed_by_name FROM partner_work_reports wr LEFT JOIN partner_companies pc ON wr.company_id = pc.id LEFT JOIN partner_schedules ps ON wr.schedule_id = ps.id LEFT JOIN sso_users su_reporter ON wr.reporter_id = su_reporter.user_id LEFT JOIN sso_users su_confirmer ON wr.confirmed_by = su_confirmer.user_id WHERE wr.id = ?`, [id]); const report = rows[0] || null; if (report) { const [workers] = await db.query( `SELECT wrw.*, pw.position FROM work_report_workers wrw LEFT JOIN partner_workers pw ON wrw.partner_worker_id = pw.id WHERE wrw.report_id = ? ORDER BY wrw.id`, [id]); report.workers = workers; } return report; } async function findByCheckin(checkinId) { const db = getPool(); const [rows] = await db.query( `SELECT wr.*, pc.company_name FROM partner_work_reports wr LEFT JOIN partner_companies pc ON wr.company_id = pc.id WHERE wr.checkin_id = ? ORDER BY wr.report_seq ASC`, [checkinId]); // 각 보고에 workers 첨부 for (const row of rows) { const [workers] = await db.query( `SELECT wrw.*, pw.position FROM work_report_workers wrw LEFT JOIN partner_workers pw ON wrw.partner_worker_id = pw.id WHERE wrw.report_id = ? ORDER BY wrw.id`, [row.id]); row.workers = workers; } return rows; } async function countByCheckin(checkinId) { const db = getPool(); const [rows] = await db.query( 'SELECT COUNT(*) AS cnt FROM partner_work_reports WHERE checkin_id = ?', [checkinId]); return rows[0].cnt; } async function create(data) { const db = getPool(); const conn = await db.getConnection(); try { await conn.beginTransaction(); // report_seq: 같은 checkin_id 내 최대값 + 1 const [seqRows] = await conn.query( 'SELECT COALESCE(MAX(report_seq), 0) + 1 AS next_seq FROM partner_work_reports WHERE checkin_id = ?', [data.checkin_id]); const reportSeq = seqRows[0].next_seq; const [result] = await conn.query( `INSERT INTO partner_work_reports (schedule_id, checkin_id, company_id, report_date, report_seq, reporter_id, actual_workers, work_content, progress_rate, issues, next_plan) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [data.schedule_id || null, data.checkin_id || null, data.company_id, data.report_date, reportSeq, data.reporter_id, data.actual_workers || null, data.work_content || null, data.progress_rate || null, data.issues || null, data.next_plan || null]); const reportId = result.insertId; // workers 삽입 if (data.workers && data.workers.length > 0) { for (const w of data.workers) { await conn.query( `INSERT INTO work_report_workers (report_id, partner_worker_id, worker_name, hours_worked) VALUES (?, ?, ?, ?)`, [reportId, w.partner_worker_id || null, w.worker_name, w.hours_worked ?? 8.0]); } } await conn.commit(); return findById(reportId); } catch (err) { await conn.rollback(); throw err; } finally { conn.release(); } } async function update(id, data) { const db = getPool(); const conn = await db.getConnection(); try { await conn.beginTransaction(); const fields = []; const values = []; if (data.actual_workers !== undefined) { fields.push('actual_workers = ?'); values.push(data.actual_workers || null); } if (data.work_content !== undefined) { fields.push('work_content = ?'); values.push(data.work_content || null); } if (data.progress_rate !== undefined) { fields.push('progress_rate = ?'); values.push(data.progress_rate || null); } if (data.issues !== undefined) { fields.push('issues = ?'); values.push(data.issues || null); } if (data.next_plan !== undefined) { fields.push('next_plan = ?'); values.push(data.next_plan || null); } if (fields.length > 0) { values.push(id); await conn.query(`UPDATE partner_work_reports SET ${fields.join(', ')} WHERE id = ?`, values); } // workers 교체 (있으면) if (data.workers !== undefined) { await conn.query('DELETE FROM work_report_workers WHERE report_id = ?', [id]); if (data.workers && data.workers.length > 0) { for (const w of data.workers) { await conn.query( `INSERT INTO work_report_workers (report_id, partner_worker_id, worker_name, hours_worked) VALUES (?, ?, ?, ?)`, [id, w.partner_worker_id || null, w.worker_name, w.hours_worked ?? 8.0]); } } } await conn.commit(); return findById(id); } catch (err) { await conn.rollback(); throw err; } finally { conn.release(); } } async function confirm(id, confirmedBy) { const db = getPool(); await db.query( 'UPDATE partner_work_reports SET confirmed_by = ?, confirmed_at = NOW() WHERE id = ? AND confirmed_by IS NULL', [confirmedBy, id]); return findById(id); } async function findAllAggregated({ company_id, schedule_id, date_from, date_to } = {}) { const db = getPool(); let sql = `SELECT wr.report_date, wr.company_id, pc.company_name, ps.id AS schedule_id, ps.work_description AS schedule_description, ps.workplace_name, COUNT(wr.id) AS report_count, (SELECT COUNT(DISTINCT wrw.worker_name) FROM work_report_workers wrw WHERE wrw.report_id IN ( SELECT wr2.id FROM partner_work_reports wr2 WHERE wr2.report_date = wr.report_date AND wr2.company_id = wr.company_id AND wr2.schedule_id = wr.schedule_id )) AS total_workers, (SELECT COALESCE(SUM(wrw.hours_worked), 0) FROM work_report_workers wrw WHERE wrw.report_id IN ( SELECT wr2.id FROM partner_work_reports wr2 WHERE wr2.report_date = wr.report_date AND wr2.company_id = wr.company_id AND wr2.schedule_id = wr.schedule_id )) AS total_hours, ROUND(AVG(wr.progress_rate), 1) AS avg_progress, SUM(CASE WHEN wr.confirmed_by IS NOT NULL THEN 1 ELSE 0 END) AS confirmed_count FROM partner_work_reports wr LEFT JOIN partner_companies pc ON wr.company_id = pc.id LEFT JOIN partner_schedules ps ON wr.schedule_id = ps.id WHERE 1=1`; const params = []; if (company_id) { sql += ' AND wr.company_id = ?'; params.push(company_id); } if (schedule_id) { sql += ' AND wr.schedule_id = ?'; params.push(schedule_id); } if (date_from) { sql += ' AND wr.report_date >= ?'; params.push(date_from); } if (date_to) { sql += ' AND wr.report_date <= ?'; params.push(date_to); } sql += ' GROUP BY wr.report_date, wr.company_id, wr.schedule_id ORDER BY wr.report_date DESC, pc.company_name'; const [rows] = await db.query(sql, params); return rows; } async function exportData({ company_id, schedule_id, date_from, date_to } = {}) { const db = getPool(); let sql = `SELECT wr.report_date, wr.report_seq, pc.company_name, ps.work_description AS schedule_description, ps.workplace_name, wr.work_content, wr.progress_rate, wr.issues, wr.next_plan, wr.actual_workers, su_reporter.name AS reporter_name, wrw.worker_name, wrw.hours_worked, CASE WHEN wr.confirmed_by IS NOT NULL THEN '확인' ELSE '미확인' END AS confirm_status, su_confirmer.name AS confirmed_by_name, wr.confirmed_at FROM partner_work_reports wr LEFT JOIN partner_companies pc ON wr.company_id = pc.id LEFT JOIN partner_schedules ps ON wr.schedule_id = ps.id LEFT JOIN sso_users su_reporter ON wr.reporter_id = su_reporter.user_id LEFT JOIN sso_users su_confirmer ON wr.confirmed_by = su_confirmer.user_id LEFT JOIN work_report_workers wrw ON wrw.report_id = wr.id WHERE 1=1`; const params = []; if (company_id) { sql += ' AND wr.company_id = ?'; params.push(company_id); } if (schedule_id) { sql += ' AND wr.schedule_id = ?'; params.push(schedule_id); } if (date_from) { sql += ' AND wr.report_date >= ?'; params.push(date_from); } if (date_to) { sql += ' AND wr.report_date <= ?'; params.push(date_to); } sql += ' ORDER BY wr.report_date DESC, pc.company_name, wr.report_seq, wrw.id'; const [rows] = await db.query(sql, params); return rows; } module.exports = { findAll, findById, findByCheckin, countByCheckin, create, update, confirm, findAllAggregated, exportData };