Files
yunhaifinance/backend/routes/verifications-new.js
T

433 lines
13 KiB
JavaScript

/**
* 验收管理路由
* 遵循设计方案:采购-付款-物流-退库一体化流程设计方案.md
* 章节:八、验收管理功能
*
* 功能:
* - 支持一次验收(直接验收)和二次验收(经集散地后验收)
* - 支持部分签收
* - 验收后自动更新项目材料库存
*/
const express = require('express');
const db = require('../db');
const router = express.Router();
/**
* 获取验收单列表
*/
router.get('/', async (req, res) => {
try {
const { purchase_order_id, project_id, status } = req.query;
let query = `
SELECT vr.*,
po.code as order_code,
p.name as project_name
FROM verification_records vr
LEFT JOIN purchase_orders po ON vr.purchase_order_id = po.id
LEFT JOIN projects p ON vr.project_id = p.id
`;
const params = [];
const conditions = [];
if (purchase_order_id) {
conditions.push('vr.purchase_order_id = $1');
params.push(purchase_order_id);
}
if (project_id) {
conditions.push('vr.project_id = $1');
params.push(project_id);
}
if (status) {
conditions.push('vr.status = $1');
params.push(status);
}
if (conditions.length > 0) {
query += ' WHERE ' + conditions.join(' AND ');
}
query += ' ORDER BY vr.created_at DESC';
const result = await db.query(query, params);
res.json({
success: true,
data: result.rows,
count: result.rows.length
});
} catch (error) {
console.error('获取验收单列表失败:', error);
res.status(500).json({
success: false,
message: '获取验收单列表失败',
error: error.message
});
}
});
/**
* 获取验收单详情
*/
router.get('/:id', async (req, res) => {
try {
const { id } = req.params;
const result = await db.query(`
SELECT vr.*,
po.code as order_code,
p.name as project_name
FROM verification_records vr
LEFT JOIN purchase_orders po ON vr.purchase_order_id = po.id
LEFT JOIN projects p ON vr.project_id = p.id
WHERE vr.id = ?
`, [id]);
if (result.rows.length === 0) {
return res.status(404).json({ success: false, message: '验收单不存在' });
}
const verification = result.rows[0];
if (verification.items) {
try {
verification.items = JSON.parse(verification.items);
} catch (e) {
verification.items = [];
}
} else {
verification.items = [];
}
res.json({
success: true,
data: verification
});
} catch (error) {
console.error('获取验收单详情失败:', error);
res.status(500).json({
success: false,
message: '获取验收单详情失败',
error: error.message
});
}
});
/**
* 创建验收单
* 遵循设计方案:支持一次验收/二次验收,支持部分签收
*/
router.post('/', async (req, res) => {
try {
const {
purchase_order_id, logistics_record_id, verification_type,
verification_date, verifier, items, project_id, storage_type,
remark, attachments
} = req.body;
const code = 'VR' + new Date().toISOString().slice(0, 10).replace(/-/g, '') +
String(Math.floor(Math.random() * 10000)).padStart(4, '0');
const itemsJson = items ? JSON.stringify(items) : null;
let totalOrdered = 0;
let totalReceived = 0;
let totalVerified = 0;
let totalRejected = 0;
if (items && Array.isArray(items)) {
for (const item of items) {
totalOrdered += item.ordered_quantity || 0;
totalReceived += item.received_quantity || 0;
totalVerified += item.verified_quantity || 0;
totalRejected += item.rejected_quantity || 0;
}
}
await db.query('BEGIN TRANSACTION');
try {
const result = await db.query(`
INSERT INTO verification_records
(code, purchase_order_id, logistics_record_id, verification_type,
verification_date, verifier, items, total_ordered, total_received,
total_verified, total_rejected, project_id, storage_type, status, remark, attachments, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?, CURRENT_TIMESTAMP)
`, [code, purchase_order_id, logistics_record_id, verification_type || 'direct',
verification_date, verifier, itemsJson, totalOrdered, totalReceived,
totalVerified, totalRejected, project_id, storage_type, remark, attachments]);
await db.query('COMMIT');
res.json({
success: true,
message: '验收单创建成功',
data: { id: (result.rows[0]?.id || result.rows?.[0]?.id), code }
});
} catch (innerError) {
await db.query('ROLLBACK');
throw innerError;
}
} catch (error) {
console.error('创建验收单失败:', error);
res.status(500).json({
success: false,
message: '创建验收单失败',
error: error.message
});
}
});
/**
* 确认验收
* 遵循设计方案:验收通过后自动更新项目材料库存
*/
router.post('/:id/confirm', async (req, res) => {
try {
const { id } = req.params;
const verificationResult = await db.query('SELECT * FROM verification_records WHERE id = $1', [id]);
if (verificationResult.rows.length === 0) {
return res.status(404).json({ success: false, message: '验收单不存在' });
}
const verification = verificationResult.rows[0];
if (verification.status !== 'pending') {
return res.status(400).json({ success: false, message: '只能确认待审核状态的验收单' });
}
await db.query('BEGIN TRANSACTION');
try {
await db.query("UPDATE verification_records SET status = 'confirmed' WHERE id = ?", [id]);
if (verification.items) {
let items;
try {
items = JSON.parse(verification.items);
} catch (e) {
items = [];
}
for (const item of items) {
if (item.verified_quantity > 0 && item.product_id) {
const existingInventory = await db.query(`
SELECT * FROM project_material_inventory
WHERE project_id = ? AND product_id = ?
`, [verification.project_id, item.product_id]);
if (existingInventory.rows.length > 0) {
const existing = existingInventory.rows[0];
const newReceivedQty = (existing.received_quantity || 0) + item.verified_quantity;
const newCurrentQty = (existing.current_quantity || 0) + item.verified_quantity;
const newTotalAmount = (existing.total_amount || 0) + (item.verified_quantity * item.unit_price || 0);
const newAvgPrice = newCurrentQty > 0 ? newTotalAmount / newCurrentQty : 0;
await db.query(`
UPDATE project_material_inventory
SET received_quantity = ?, current_quantity = ?, total_amount = ?, average_price = ?, updated_at = CURRENT_TIMESTAMP
WHERE project_id = ? AND product_id = ?
`, [newReceivedQty, newCurrentQty, newTotalAmount, newAvgPrice, verification.project_id, item.product_id]);
} else {
await db.query(`
INSERT INTO project_material_inventory
(project_id, product_id, product_name, unit, received_quantity, current_quantity, total_amount, average_price, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
`, [verification.project_id, item.product_id, item.product_name, item.unit,
item.verified_quantity, item.verified_quantity,
item.verified_quantity * item.unit_price || 0, item.unit_price || 0]);
}
}
}
}
await db.query(`
UPDATE purchase_orders
SET status = 'verified', updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`, [verification.purchase_order_id]);
await db.query('COMMIT');
res.json({
success: true,
message: '验收确认成功,已更新项目材料库存'
});
} catch (innerError) {
await db.query('ROLLBACK');
throw innerError;
}
} catch (error) {
console.error('确认验收失败:', error);
res.status(500).json({
success: false,
message: '确认验收失败',
error: error.message
});
}
});
/**
* 驳回验收
*/
router.post('/:id/reject', async (req, res) => {
try {
const { id } = req.params;
const { reason } = req.body;
const result = await db.query(`
UPDATE verification_records
SET status = 'rejected', remark = COALESCE(remark || ' | ', '') || '驳回原因: ' || ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`, [reason || '无', id]);
if (result.changes === 0) {
return res.status(404).json({ success: false, message: '验收单不存在' });
}
res.json({ success: true, message: '验收已驳回' });
} catch (error) {
console.error('驳回验收失败:', error);
res.status(500).json({ success: false, message: '驳回验收失败', error: error.message });
}
});
/**
* 更新验收单
*/
router.put('/:id', async (req, res) => {
try {
const { id } = req.params;
const { items, verification_date, verifier, remark, attachments } = req.body;
const verificationResult = await db.query('SELECT * FROM verification_records WHERE id = $1', [id]);
if (verificationResult.rows.length === 0) {
return res.status(404).json({ success: false, message: '验收单不存在' });
}
const verification = verificationResult.rows[0];
if (verification.status !== 'pending') {
return res.status(400).json({ success: false, message: '只能修改待审核状态的验收单' });
}
const itemsJson = items ? JSON.stringify(items) : null;
let totalOrdered = 0;
let totalReceived = 0;
let totalVerified = 0;
let totalRejected = 0;
if (items && Array.isArray(items)) {
for (const item of items) {
totalOrdered += item.ordered_quantity || 0;
totalReceived += item.received_quantity || 0;
totalVerified += item.verified_quantity || 0;
totalRejected += item.rejected_quantity || 0;
}
}
await db.query(`
UPDATE verification_records
SET items = ?, verification_date = ?, verifier = ?,
total_ordered = ?, total_received = ?, total_verified = ?, total_rejected = ?,
remark = ?, attachments = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`, [itemsJson, verification_date, verifier, totalOrdered, totalReceived, totalVerified, totalRejected, remark, attachments, id]);
res.json({ success: true, message: '验收单更新成功' });
} catch (error) {
console.error('更新验收单失败:', error);
res.status(500).json({
success: false,
message: '更新验收单失败',
error: error.message
});
}
});
/**
* 删除验收单
*/
router.delete('/:id', async (req, res) => {
try {
const { id } = req.params;
const verificationResult = await db.query('SELECT * FROM verification_records WHERE id = $1', [id]);
if (verificationResult.rows.length === 0) {
return res.status(404).json({ success: false, message: '验收单不存在' });
}
const verification = verificationResult.rows[0];
if (verification.status !== 'pending') {
return res.status(400).json({ success: false, message: '只能删除待审核状态的验收单' });
}
await db.query('DELETE FROM verification_records WHERE id = $1', [id]);
res.json({ success: true, message: '验收单删除成功' });
} catch (error) {
console.error('删除验收单失败:', error);
res.status(500).json({
success: false,
message: '删除验收单失败',
error: error.message
});
}
});
/**
* 获取订单可验收的商品明细
*/
router.get('/order-items/:orderId', async (req, res) => {
try {
const { orderId } = req.params;
const itemsResult = await db.query(`
SELECT poi.*, p.name as product_name
FROM purchase_order_items poi
LEFT JOIN products p ON poi.product_id = p.id
WHERE poi.order_id = ?
`, [orderId]);
const verifiedResult = await db.query(`
SELECT items
FROM verification_records
WHERE purchase_order_id = ? AND status = 'confirmed'
`, [orderId]);
const verifiedQty = {};
for (const row of verifiedResult.rows) {
if (row.items) {
try {
const items = JSON.parse(row.items);
for (const item of items) {
const key = item.product_id || item.product_name;
verifiedQty[key] = (verifiedQty[key] || 0) + (item.verified_quantity || 0);
}
} catch (e) {}
}
}
const items = itemsResult.rows.map(item => ({
...item,
already_verified: verifiedQty[item.product_id || item.product_name] || 0,
pending_verify: (item.quantity || 0) - (verifiedQty[item.product_id || item.product_name] || 0)
}));
res.json({
success: true,
data: items
});
} catch (error) {
console.error('获取订单商品明细失败:', error);
res.status(500).json({
success: false,
message: '获取订单商品明细失败',
error: error.message
});
}
});
module.exports = router;