feat(耗材管理): 新增创建耗材同时入库功能并优化库存编辑逻辑
添加创建耗材时同时入库的功能,包含入库数量、操作人和原因等字段 强制初始化新建耗材的库存为0和空SN列表 禁止通过编辑接口直接修改库存和SN列表 前端新增同时入库选项及相关表单字段 优化编辑模式下库存信息的展示方式
This commit is contained in:
+131
-10
@@ -122,14 +122,9 @@ router.post('/', async (req, res) => {
|
||||
const consumableData = {
|
||||
...req.body,
|
||||
consumableId: req.body.consumableId || `CON${Date.now()}`,
|
||||
currentStock: 0, // 强制设为0
|
||||
snList: [], // 强制设为空数组
|
||||
};
|
||||
// SN 列表非空时,取手动填写的库存和 SN 数量中的较大值
|
||||
if (Array.isArray(consumableData.snList) && consumableData.snList.length > 0) {
|
||||
consumableData.currentStock = Math.max(
|
||||
Number(consumableData.currentStock) || 0,
|
||||
consumableData.snList.length
|
||||
);
|
||||
}
|
||||
const consumable = await Consumable.create(consumableData, { transaction });
|
||||
|
||||
await ConsumableLog.create(
|
||||
@@ -170,6 +165,132 @@ router.post('/', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// 创建耗材并同时入库
|
||||
router.post('/create-with-inbound', async (req, res) => {
|
||||
const transaction = await sequelize.transaction();
|
||||
try {
|
||||
const {
|
||||
consumableId,
|
||||
name,
|
||||
category,
|
||||
unit,
|
||||
currentStock,
|
||||
minStock,
|
||||
maxStock,
|
||||
unitPrice,
|
||||
supplier,
|
||||
location,
|
||||
description,
|
||||
status,
|
||||
// 入库相关字段
|
||||
inboundQuantity,
|
||||
inboundOperator,
|
||||
inboundReason,
|
||||
inboundNotes,
|
||||
inboundSnList,
|
||||
} = req.body;
|
||||
|
||||
// 创建耗材
|
||||
const consumable = await Consumable.create({
|
||||
consumableId: consumableId || `CON${Date.now()}`,
|
||||
name,
|
||||
category,
|
||||
unit: unit || '个',
|
||||
currentStock: 0,
|
||||
minStock: minStock || 10,
|
||||
maxStock: maxStock || 0,
|
||||
unitPrice: unitPrice || 0,
|
||||
supplier: supplier || '',
|
||||
location: location || '',
|
||||
description: description || '',
|
||||
status: status || 'active',
|
||||
snList: [],
|
||||
}, { transaction });
|
||||
|
||||
// 记录创建日志
|
||||
await ConsumableLog.create({
|
||||
consumableId: consumable.consumableId,
|
||||
consumableName: consumable.name,
|
||||
operationType: 'create',
|
||||
quantity: 0,
|
||||
previousStock: 0,
|
||||
currentStock: 0,
|
||||
operator: inboundOperator || '系统',
|
||||
reason: '新建耗材',
|
||||
notes: description || '',
|
||||
consumableSnapshot: {
|
||||
category: consumable.category,
|
||||
unit: consumable.unit,
|
||||
unitPrice: consumable.unitPrice,
|
||||
supplier: consumable.supplier,
|
||||
location: consumable.location,
|
||||
minStock: consumable.minStock,
|
||||
maxStock: consumable.maxStock,
|
||||
},
|
||||
}, { transaction });
|
||||
|
||||
// 执行入库操作
|
||||
if (inboundQuantity && inboundQuantity > 0) {
|
||||
const previousStock = 0;
|
||||
const newStock = inboundQuantity;
|
||||
let updatedSnList = [...(inboundSnList || [])];
|
||||
|
||||
await Consumable.update({
|
||||
currentStock: newStock,
|
||||
snList: updatedSnList,
|
||||
version: sequelize.literal('version + 1'),
|
||||
}, {
|
||||
where: { consumableId: consumable.consumableId },
|
||||
transaction,
|
||||
});
|
||||
|
||||
await ConsumableRecord.create({
|
||||
consumableId: consumable.consumableId,
|
||||
type: 'in',
|
||||
quantity: inboundQuantity,
|
||||
previousStock,
|
||||
currentStock: newStock,
|
||||
operator: inboundOperator || '系统',
|
||||
reason: inboundReason || '初始入库',
|
||||
notes: inboundNotes || '',
|
||||
snList: updatedSnList,
|
||||
}, { transaction });
|
||||
|
||||
await ConsumableLog.create({
|
||||
consumableId: consumable.consumableId,
|
||||
consumableName: consumable.name,
|
||||
operationType: 'in',
|
||||
quantity: inboundQuantity,
|
||||
previousStock,
|
||||
currentStock: newStock,
|
||||
operator: inboundOperator || '系统',
|
||||
reason: inboundReason || '初始入库',
|
||||
notes: inboundNotes || '',
|
||||
snList: updatedSnList,
|
||||
consumableSnapshot: {
|
||||
category: consumable.category,
|
||||
unit: consumable.unit,
|
||||
unitPrice: consumable.unitPrice,
|
||||
supplier: consumable.supplier,
|
||||
location: consumable.location,
|
||||
},
|
||||
}, { transaction });
|
||||
}
|
||||
|
||||
await transaction.commit();
|
||||
res.status(201).json(consumable);
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('创建耗材并入库错误:', error);
|
||||
if (error.name === 'SequelizeValidationError') {
|
||||
const messages = error.errors.map(e => `${e.path}: ${e.message}`).join(', ');
|
||||
res.status(400).json({ error: `Validation error: ${messages}` });
|
||||
} else {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/import', async (req, res) => {
|
||||
const transaction = await sequelize.transaction();
|
||||
try {
|
||||
@@ -1272,9 +1393,9 @@ router.put('/:id', async (req, res) => {
|
||||
|
||||
const oldData = consumable.toJSON();
|
||||
const updateData = { ...req.body };
|
||||
if (Array.isArray(updateData.snList)) {
|
||||
updateData.currentStock = updateData.snList.length;
|
||||
}
|
||||
// 禁止通过编辑接口修改库存和SN列表
|
||||
delete updateData.currentStock;
|
||||
delete updateData.snList;
|
||||
await consumable.update(updateData, { transaction });
|
||||
|
||||
await ConsumableLog.create(
|
||||
|
||||
Reference in New Issue
Block a user