const PurchaseBatchModel = require('../models/purchaseBatchModel'); const PurchaseRequestModel = require('../models/purchaseRequestModel'); const { saveBase64Image } = require('../services/imageUploadService'); const logger = require('../utils/logger'); const notifyHelper = require('../../../shared/utils/notifyHelper'); const PurchaseBatchController = { getAll: async (req, res) => { try { const { status } = req.query; const rows = await PurchaseBatchModel.getAll({ status }); res.json({ success: true, data: rows }); } catch (err) { logger.error('PurchaseBatch getAll error:', err); res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' }); } }, getById: async (req, res) => { try { const batch = await PurchaseBatchModel.getById(req.params.id); if (!batch) return res.status(404).json({ success: false, message: '그룹을 찾을 수 없습니다.' }); // 포함된 요청 목록도 함께 반환 const requests = await PurchaseRequestModel.getAll({ batch_id: req.params.id }); res.json({ success: true, data: { ...batch, requests } }); } catch (err) { logger.error('PurchaseBatch getById error:', err); res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' }); } }, // 그룹 생성 + 요청 포함 create: async (req, res) => { try { const { batch_name, category, vendor_id, notes, request_ids } = req.body; if (!request_ids || !request_ids.length) { return res.status(400).json({ success: false, message: '그룹에 포함할 신청 건을 선택해주세요.' }); } const batchId = await PurchaseBatchModel.create({ batchName: batch_name, category, vendorId: vendor_id, notes, createdBy: req.user.id }); await PurchaseBatchModel.addRequests(batchId, request_ids); // 신청자들에게 알림 const requesterIds = await PurchaseRequestModel.getRequesterIdsByBatch(batchId); if (requesterIds.length > 0) { notifyHelper.send({ type: 'purchase', title: '구매 진행 안내', message: '신청하신 소모품 구매가 진행됩니다.', link_url: '/pages/purchase/request-mobile.html', target_user_ids: requesterIds, created_by: req.user.id }).catch(() => {}); } const batch = await PurchaseBatchModel.getById(batchId); res.status(201).json({ success: true, data: batch, message: '그룹이 생성되었습니다.' }); } catch (err) { logger.error('PurchaseBatch create error:', err); res.status(400).json({ success: false, message: err.message || '서버 오류가 발생했습니다.' }); } }, update: async (req, res) => { try { const { batch_name, category, vendor_id, notes, add_request_ids, remove_request_ids } = req.body; const batch = await PurchaseBatchModel.getById(req.params.id); if (!batch) return res.status(404).json({ success: false, message: '그룹을 찾을 수 없습니다.' }); if (batch_name !== undefined || category !== undefined || vendor_id !== undefined || notes !== undefined) { await PurchaseBatchModel.update(req.params.id, { batchName: batch_name !== undefined ? batch_name : batch.batch_name, category: category !== undefined ? category : batch.category, vendorId: vendor_id !== undefined ? vendor_id : batch.vendor_id, notes: notes !== undefined ? notes : batch.notes }); } if (add_request_ids && add_request_ids.length) { await PurchaseBatchModel.addRequests(req.params.id, add_request_ids); } if (remove_request_ids && remove_request_ids.length) { await PurchaseBatchModel.removeRequests(req.params.id, remove_request_ids); } const updated = await PurchaseBatchModel.getById(req.params.id); res.json({ success: true, data: updated, message: '그룹이 수정되었습니다.' }); } catch (err) { logger.error('PurchaseBatch update error:', err); res.status(400).json({ success: false, message: err.message || '서버 오류가 발생했습니다.' }); } }, delete: async (req, res) => { try { const deleted = await PurchaseBatchModel.delete(req.params.id); if (!deleted) return res.status(400).json({ success: false, message: '대기 상태의 그룹만 삭제할 수 있습니다.' }); res.json({ success: true, message: '그룹이 삭제되었습니다.' }); } catch (err) { logger.error('PurchaseBatch delete error:', err); res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' }); } }, // 그룹 일괄 구매 처리 purchase: async (req, res) => { try { const batch = await PurchaseBatchModel.getById(req.params.id); if (!batch) return res.status(404).json({ success: false, message: '그룹을 찾을 수 없습니다.' }); if (batch.status !== 'pending') { return res.status(400).json({ success: false, message: '대기 상태의 그룹만 구매 처리할 수 있습니다.' }); } // batch 내 모든 요청 purchased 전환 await PurchaseRequestModel.markBatchPurchased(req.params.id); await PurchaseBatchModel.markPurchased(req.params.id, req.user.id); res.json({ success: true, message: '일괄 구매 처리가 완료되었습니다.' }); } catch (err) { logger.error('PurchaseBatch purchase error:', err); res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' }); } }, // 그룹 일괄 입고 처리 receive: async (req, res) => { try { const batch = await PurchaseBatchModel.getById(req.params.id); if (!batch) return res.status(404).json({ success: false, message: '그룹을 찾을 수 없습니다.' }); if (batch.status !== 'purchased') { return res.status(400).json({ success: false, message: '구매완료 상태의 그룹만 입고 처리할 수 있습니다.' }); } const { received_location, photo } = req.body; let receivedPhotoPath = null; if (photo) { receivedPhotoPath = await saveBase64Image(photo, 'received', 'purchase_received'); } await PurchaseRequestModel.receiveBatch(req.params.id, { receivedPhotoPath, receivedLocation: received_location, receivedBy: req.user.id }); await PurchaseBatchModel.markReceived(req.params.id, req.user.id); // 신청자들에게 입고 알림 const requesterIds = await PurchaseRequestModel.getRequesterIdsByBatch(req.params.id); if (requesterIds.length > 0) { notifyHelper.send({ type: 'purchase', title: '소모품 입고 완료', message: `소모품이 입고되었습니다.${received_location ? ' 보관위치: ' + received_location : ''}`, link_url: '/pages/purchase/request-mobile.html', target_user_ids: requesterIds, created_by: req.user.id }).catch(() => {}); } res.json({ success: true, message: '일괄 입고 처리가 완료되었습니다.' }); } catch (err) { logger.error('PurchaseBatch receive error:', err); res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' }); } } }; module.exports = PurchaseBatchController;