Files
tk-factory-services/system1-factory/api/controllers/purchaseController.js
Hyungi Ahn cae735f243 feat(purchase): 구매신청 검색/직접입력/사진첨부/HEIC 지원/마스터 자동등록
- 소모품 select → 검색형 드롭다운 (debounce + 키보드 탐색)
- 미등록 품목 직접 입력 + 분류 선택 지원
- 사진 첨부 (base64 업로드, HEIC→JPEG 프론트 변환)
- 구매 처리 시 미등록 품목 소모품 마스터 자동 등록
- item_id NULL 허용, LEFT JOIN, custom_item_name/custom_category/photo_path 컬럼
- DB 마이그레이션 필요: ALTER TABLE purchase_requests

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

127 lines
5.0 KiB
JavaScript

const PurchaseModel = require('../models/purchaseModel');
const PurchaseRequestModel = require('../models/purchaseRequestModel');
const logger = require('../utils/logger');
const PurchaseController = {
// 구매 처리 (신청 → 구매)
create: async (req, res) => {
try {
const { request_id, item_id, vendor_id, quantity, unit_price, purchase_date, update_base_price, register_to_master, notes } = req.body;
// item_id가 없으면 custom item → register_to_master로 자동 등록 가능
let effectiveItemId = item_id;
if (!effectiveItemId && request_id) {
// 미등록 품목의 구매 처리 — 마스터 등록 처리
const requestData = await PurchaseRequestModel.getById(request_id);
if (requestData && requestData.custom_item_name) {
if (register_to_master !== false) {
// 마스터에 등록
const newItemId = await PurchaseModel.registerToMaster(
requestData.custom_item_name,
requestData.custom_category,
null // maker
);
effectiveItemId = newItemId;
// purchase_requests.item_id 업데이트
await PurchaseRequestModel.updateItemId(request_id, newItemId);
}
}
}
if (!effectiveItemId) return res.status(400).json({ success: false, message: '소모품을 선택해주세요.' });
if (!unit_price) return res.status(400).json({ success: false, message: '구매 단가를 입력해주세요.' });
if (!purchase_date) return res.status(400).json({ success: false, message: '구매일을 입력해주세요.' });
// 구매 내역 생성
const purchaseId = await PurchaseModel.createFromRequest({
request_id: request_id || null,
item_id: effectiveItemId,
vendor_id: vendor_id || null,
quantity: quantity || 1,
unit_price,
purchase_date,
purchaser_id: req.user.id,
notes
});
// 기준가 업데이트 요청 시
if (update_base_price) {
const items = await PurchaseModel.getConsumableItems(false);
const item = items.find(i => i.item_id === parseInt(effectiveItemId));
if (item) {
await PurchaseModel.updateBasePrice(effectiveItemId, unit_price, item.base_price, req.user.id);
}
}
// 설비 자동 등록 (category='equipment')
let equipmentResult = null;
if (request_id) {
const requestData = await PurchaseRequestModel.getById(request_id);
const category = requestData?.category || requestData?.custom_category;
if (category === 'equipment') {
equipmentResult = await PurchaseModel.tryAutoRegisterEquipment({
item_name: requestData.item_name || requestData.custom_item_name,
maker: requestData.maker,
vendor_name: null,
unit_price,
purchase_date,
purchase_id: purchaseId,
purchaser_id: req.user.id
});
}
} else {
// 직접 구매 시에도 category 확인
const items = await PurchaseModel.getConsumableItems(false);
const item = items.find(i => i.item_id === parseInt(effectiveItemId));
if (item && item.category === 'equipment') {
const vendors = await PurchaseModel.getVendors();
const vendor = vendors.find(v => v.vendor_id === parseInt(vendor_id));
equipmentResult = await PurchaseModel.tryAutoRegisterEquipment({
item_name: item.item_name,
maker: item.maker,
vendor_name: vendor ? vendor.vendor_name : null,
unit_price,
purchase_date,
purchase_id: purchaseId,
purchaser_id: req.user.id
});
}
}
const result = { purchase_id: purchaseId };
if (equipmentResult) result.equipment = equipmentResult;
res.status(201).json({ success: true, data: result, message: '구매 처리가 완료되었습니다.' });
} catch (err) {
logger.error('Purchase create error:', err);
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' });
}
},
// 구매 내역 목록
getAll: async (req, res) => {
try {
const { vendor_id, category, from_date, to_date, year_month } = req.query;
const rows = await PurchaseModel.getAll({ vendor_id, category, from_date, to_date, year_month });
res.json({ success: true, data: rows });
} catch (err) {
logger.error('Purchase getAll error:', err);
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' });
}
},
// 가격 변동 이력
getPriceHistory: async (req, res) => {
try {
const rows = await PurchaseModel.getPriceHistory(req.params.itemId);
res.json({ success: true, data: rows });
} catch (err) {
logger.error('PriceHistory get error:', err);
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다.' });
}
}
};
module.exports = PurchaseController;