/** * 验收管理路由 * 遵循设计方案:采购-付款-物流-退库一体化流程设计方案.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: process.env.NODE_ENV === 'development' ? 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: process.env.NODE_ENV === 'development' ? 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 ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, 'pending', $14, $15, CURRENT_TIMESTAMP) RETURNING id`, [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: process.env.NODE_ENV === 'development' ? 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 ($1, $2, $3, $4, $5, $6, $7, $8, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) RETURNING id`, [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: process.env.NODE_ENV === 'development' ? 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.rowCount === 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: '驳回验收失败' }); } }); /** * 更新验收单 */ 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: process.env.NODE_ENV === 'development' ? 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: process.env.NODE_ENV === 'development' ? 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: process.env.NODE_ENV === 'development' ? error.message : '操作失败' }); } }); module.exports = router;