diff --git a/user-management/api/controllers/partnerController.js b/user-management/api/controllers/partnerController.js index 479330f..17e9905 100644 --- a/user-management/api/controllers/partnerController.js +++ b/user-management/api/controllers/partnerController.js @@ -119,7 +119,32 @@ async function deactivateWorker(req, res) { } } +async function getDeleteInfo(req, res) { + try { + const company = await partnerModel.findById(req.params.id); + if (!company) return res.status(404).json({ success: false, error: '업체를 찾을 수 없습니다' }); + const info = await partnerModel.getDeleteInfo(req.params.id); + res.json({ success: true, data: { company_name: company.company_name, ...info } }); + } catch (err) { + console.error('Partner getDeleteInfo error:', err); + res.status(500).json({ success: false, error: err.message }); + } +} + +async function permanentDelete(req, res) { + try { + const company = await partnerModel.findById(req.params.id); + if (!company) return res.status(404).json({ success: false, error: '업체를 찾을 수 없습니다' }); + await partnerModel.permanentDelete(req.params.id, req.user.id); + res.json({ success: true, message: `"${company.company_name}" 업체가 완전히 삭제되었습니다` }); + } catch (err) { + console.error('Partner permanentDelete error:', err); + res.status(500).json({ success: false, error: err.message }); + } +} + module.exports = { list, getById, create, update, deactivate, - listWorkers, createWorker, updateWorker, deactivateWorker + listWorkers, createWorker, updateWorker, deactivateWorker, + getDeleteInfo, permanentDelete }; diff --git a/user-management/api/models/partnerModel.js b/user-management/api/models/partnerModel.js index 98d1b3f..a3169ef 100644 --- a/user-management/api/models/partnerModel.js +++ b/user-management/api/models/partnerModel.js @@ -109,7 +109,108 @@ async function deactivateWorker(id) { await db.query('UPDATE partner_workers SET is_active = FALSE WHERE id = ?', [id]); } +async function getDeleteInfo(companyId) { + const db = getPool(); + const [[workers]] = await db.query('SELECT COUNT(*) as cnt FROM partner_workers WHERE company_id = ?', [companyId]); + const [[schedules]] = await db.query('SELECT COUNT(*) as cnt FROM partner_schedules WHERE company_id = ?', [companyId]); + const [[checkins]] = await db.query('SELECT COUNT(*) as cnt FROM partner_work_checkins WHERE company_id = ?', [companyId]); + const [[reports]] = await db.query('SELECT COUNT(*) as cnt FROM partner_work_reports WHERE company_id = ?', [companyId]); + const [[visits]] = await db.query('SELECT COUNT(*) as cnt FROM daily_visits WHERE company_id = ?', [companyId]); + const [[accounts]] = await db.query('SELECT COUNT(*) as cnt FROM sso_users WHERE partner_company_id = ?', [companyId]); + + // 삭제 차단 조건: 해당 업체 SSO 계정이 구매 이력을 가지고 있는지 + const [userRows] = await db.query('SELECT id FROM sso_users WHERE partner_company_id = ?', [companyId]); + const userIds = userRows.map(r => r.id); + let purchaseRequests = 0; + let purchases = 0; + if (userIds.length > 0) { + const [[pr]] = await db.query('SELECT COUNT(*) as cnt FROM purchase_requests WHERE requester_id IN (?)', [userIds]); + const [[pu]] = await db.query('SELECT COUNT(*) as cnt FROM purchases WHERE purchaser_id IN (?)', [userIds]); + purchaseRequests = pr.cnt; + purchases = pu.cnt; + } + + return { + workers: workers.cnt, + schedules: schedules.cnt, + checkins: checkins.cnt, + reports: reports.cnt, + visits: visits.cnt, + accounts: accounts.cnt, + purchaseRequests, + purchases + }; +} + +async function permanentDelete(companyId, requestUserId) { + const db = getPool(); + + // 사전 체크: 구매 이력 확인 + const [userRows] = await db.query('SELECT id FROM sso_users WHERE partner_company_id = ?', [companyId]); + const userIds = userRows.map(r => r.id); + if (userIds.length > 0) { + const [[pr]] = await db.query('SELECT COUNT(*) as cnt FROM purchase_requests WHERE requester_id IN (?)', [userIds]); + const [[pu]] = await db.query('SELECT COUNT(*) as cnt FROM purchases WHERE purchaser_id IN (?)', [userIds]); + if (pr.cnt > 0 || pu.cnt > 0) { + throw new Error('구매 이력이 있어 삭제할 수 없습니다. 비활성화를 이용해주세요.'); + } + } + + // 작업자 ID 조회 + const [workerRows] = await db.query('SELECT id FROM partner_workers WHERE company_id = ?', [companyId]); + const workerIds = workerRows.map(r => r.id); + + const conn = await db.getConnection(); + try { + await conn.beginTransaction(); + + // 1. work_report_workers에서 해당 업체 작업자 참조 SET NULL + if (workerIds.length > 0) { + await conn.query('UPDATE work_report_workers SET partner_worker_id = NULL WHERE partner_worker_id IN (?)', [workerIds]); + } + + // 2. partner_work_reports 삭제 (→ work_report_workers CASCADE 자동) + await conn.query('DELETE FROM partner_work_reports WHERE company_id = ?', [companyId]); + + // 3. partner_work_checkins 삭제 + await conn.query('DELETE FROM partner_work_checkins WHERE company_id = ?', [companyId]); + + // 4. partner_schedules 삭제 + await conn.query('DELETE FROM partner_schedules WHERE company_id = ?', [companyId]); + + // 5. daily_visit_workers에서 해당 업체 작업자 참조 SET NULL + if (workerIds.length > 0) { + await conn.query('UPDATE daily_visit_workers SET partner_worker_id = NULL WHERE partner_worker_id IN (?)', [workerIds]); + } + + // 6. daily_visits는 SET NULL 자동 (방문기록 보존) + + // 7. partner_workers 삭제 + await conn.query('DELETE FROM partner_workers WHERE company_id = ?', [companyId]); + + // 8. 협력업체 SSO 계정 처리 + if (userIds.length > 0) { + await conn.query('UPDATE sp_vacation_requests SET reviewed_by = NULL WHERE reviewed_by IN (?)', [userIds]); + await conn.query('DELETE FROM sp_vacation_requests WHERE user_id IN (?)', [userIds]); + await conn.query('UPDATE sp_vacation_balances SET created_by = ? WHERE created_by IN (?)', [requestUserId, userIds]); + await conn.query('DELETE FROM sp_vacation_balances WHERE user_id IN (?)', [userIds]); + await conn.query('DELETE FROM sso_users WHERE partner_company_id = ?', [companyId]); + } + + // 9. partner_companies 삭제 + await conn.query('DELETE FROM partner_companies WHERE id = ?', [companyId]); + + await conn.commit(); + } catch (err) { + await conn.rollback(); + throw err; + } finally { + conn.release(); + } +} + module.exports = { findAll, findById, create, update, deactivate, - findWorkersByCompany, findWorkerById, createWorker, updateWorker, deactivateWorker + findWorkersByCompany, findWorkerById, createWorker, updateWorker, deactivateWorker, + getDeleteInfo, permanentDelete }; diff --git a/user-management/api/routes/partnerRoutes.js b/user-management/api/routes/partnerRoutes.js index a3d8c72..95b7921 100644 --- a/user-management/api/routes/partnerRoutes.js +++ b/user-management/api/routes/partnerRoutes.js @@ -6,6 +6,8 @@ const ctrl = require('../controllers/partnerController'); router.use(requireAuth); router.get('/', ctrl.list); +router.get('/:id/delete-info', requireAdmin, ctrl.getDeleteInfo); +router.delete('/:id/permanent', requireAdmin, ctrl.permanentDelete); router.get('/:id', ctrl.getById); router.post('/', requireAdmin, ctrl.create); router.put('/:id', requireAdmin, ctrl.update); diff --git a/user-management/web/index.html b/user-management/web/index.html index 5c73df9..3fc0023 100644 --- a/user-management/web/index.html +++ b/user-management/web/index.html @@ -2009,7 +2009,7 @@ - + diff --git a/user-management/web/static/js/tkuser-partners.js b/user-management/web/static/js/tkuser-partners.js index 587c0ba..7e9dfa9 100644 --- a/user-management/web/static/js/tkuser-partners.js +++ b/user-management/web/static/js/tkuser-partners.js @@ -56,6 +56,7 @@ function renderPartnersListTkuser() { ${isAdmin ? `