Files
tk-factory-services/system1-factory/api/controllers/purchaseRequestController.js
Hyungi Ahn 3623551a6b feat(purchase): 생산소모품 구매 관리 시스템 구현
tkuser: 업체(공급업체) CRUD + 소모품 마스터 CRUD (사진 업로드 포함)
tkfb: 구매신청 → 구매 처리 → 월간 분석/정산 전체 워크플로
설비(equipment) 분류 구매 시 자동 등록 + 실패 시 admin 알림

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:21:59 +09:00

121 lines
5.1 KiB
JavaScript

const PurchaseRequestModel = require('../models/purchaseRequestModel');
const PurchaseModel = require('../models/purchaseModel');
const logger = require('../utils/logger');
const PurchaseRequestController = {
// 구매신청 목록
getAll: async (req, res) => {
try {
const { status, category, from_date, to_date } = req.query;
const isAdmin = req.user && ['admin', 'system'].includes(req.user.access_level);
const filters = { status, category, from_date, to_date };
if (!isAdmin) filters.requester_id = req.user.id;
const rows = await PurchaseRequestModel.getAll(filters);
res.json({ success: true, data: rows });
} catch (err) {
logger.error('PurchaseRequest getAll error:', err);
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' });
}
},
// 구매신청 상세
getById: async (req, res) => {
try {
const row = await PurchaseRequestModel.getById(req.params.id);
if (!row) return res.status(404).json({ success: false, message: '신청 건을 찾을 수 없습니다.' });
res.json({ success: true, data: row });
} catch (err) {
logger.error('PurchaseRequest getById error:', err);
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' });
}
},
// 구매신청 생성
create: async (req, res) => {
try {
const { item_id, quantity, notes } = req.body;
if (!item_id) return res.status(400).json({ success: false, message: '소모품을 선택해주세요.' });
if (!quantity || quantity < 1) return res.status(400).json({ success: false, message: '수량은 1 이상이어야 합니다.' });
const request = await PurchaseRequestModel.create({
item_id,
quantity,
requester_id: req.user.id,
request_date: new Date().toISOString().substring(0, 10),
notes
});
res.status(201).json({ success: true, data: request, message: '구매신청이 등록되었습니다.' });
} catch (err) {
logger.error('PurchaseRequest create error:', err);
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' });
}
},
// 보류 처리 (admin)
hold: async (req, res) => {
try {
const { hold_reason } = req.body;
const request = await PurchaseRequestModel.hold(req.params.id, hold_reason);
if (!request) return res.status(404).json({ success: false, message: '신청 건을 찾을 수 없습니다.' });
res.json({ success: true, data: request, message: '보류 처리되었습니다.' });
} catch (err) {
logger.error('PurchaseRequest hold error:', err);
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' });
}
},
// pending으로 되돌리기 (admin)
revert: async (req, res) => {
try {
const request = await PurchaseRequestModel.revertToPending(req.params.id);
if (!request) return res.status(404).json({ success: false, message: '신청 건을 찾을 수 없습니다.' });
res.json({ success: true, data: request, message: '대기 상태로 되돌렸습니다.' });
} catch (err) {
logger.error('PurchaseRequest revert error:', err);
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' });
}
},
// 삭제 (본인 + pending만)
delete: async (req, res) => {
try {
const existing = await PurchaseRequestModel.getById(req.params.id);
if (!existing) return res.status(404).json({ success: false, message: '신청 건을 찾을 수 없습니다.' });
const isAdmin = req.user && ['admin', 'system'].includes(req.user.access_level);
if (!isAdmin && existing.requester_id !== req.user.id) {
return res.status(403).json({ success: false, message: '본인의 신청만 삭제할 수 있습니다.' });
}
const deleted = await PurchaseRequestModel.delete(req.params.id);
if (!deleted) return res.status(400).json({ success: false, message: '대기 상태의 신청만 삭제할 수 있습니다.' });
res.json({ success: true, message: '삭제되었습니다.' });
} catch (err) {
logger.error('PurchaseRequest delete error:', err);
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' });
}
},
// 소모품 목록 (select용)
getConsumableItems: async (req, res) => {
try {
const items = await PurchaseModel.getConsumableItems();
res.json({ success: true, data: items });
} catch (err) {
logger.error('ConsumableItems get error:', err);
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' });
}
},
// 업체 목록 (select용)
getVendors: async (req, res) => {
try {
const vendors = await PurchaseModel.getVendors();
res.json({ success: true, data: vendors });
} catch (err) {
logger.error('Vendors get error:', err);
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' });
}
}
};
module.exports = PurchaseRequestController;