/** * 退库管理路由 * 遵循设计方案:采购-付款-物流-退库一体化流程设计方案.md * 章节:九、项目材料管理 - 退库记录 * * 功能: * - 支持退回供应商和退回仓库两种类型 * - 退库后自动更新项目材料库存 * - 支持成本调整和退款处理 */ const express = require('express'); const db = require('../db'); const router = express.Router(); /** * 获取退库单列表 */ router.get('/', async (req, res) => { try { const { project_id, status, return_type } = req.query; let query = ` SELECT rr.*, p.name as project_name FROM return_records rr LEFT JOIN projects p ON rr.project_id = p.id `; const params = []; const conditions = []; if (project_id) { conditions.push('rr.project_id = $1'); params.push(project_id); } if (status) { conditions.push('rr.status = $1'); params.push(status); } if (return_type) { conditions.push('rr.return_type = $1'); params.push(return_type); } if (conditions.length > 0) { query += ' WHERE ' + conditions.join(' AND '); } query += ' ORDER BY rr.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 rr.*, p.name as project_name FROM return_records rr LEFT JOIN projects p ON rr.project_id = p.id WHERE rr.id = ? `, [id]); if (result.rows.length === 0) { return res.status(404).json({ success: false, message: '退库单不存在' }); } const returnRecord = result.rows[0]; if (returnRecord.items) { try { returnRecord.items = JSON.parse(returnRecord.items); } catch (e) { returnRecord.items = []; } } else { returnRecord.items = []; } res.json({ success: true, data: returnRecord }); } 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 { project_id, return_type, return_date, applicant, items, total_quantity, total_amount, cost_adjustment, refund_amount, remark, attachments } = req.body; const code = 'RT' + 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 calcTotalQty = 0; let calcTotalAmt = 0; if (items && Array.isArray(items)) { for (const item of items) { calcTotalQty += item.quantity || 0; calcTotalAmt += item.amount || 0; } } const result = await db.query(` INSERT INTO return_records (code, project_id, return_type, return_date, applicant, items, total_quantity, total_amount, cost_adjustment, refund_amount, status, remark, attachments, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'pending', $11, $12, CURRENT_TIMESTAMP) RETURNING id`, [code, project_id, return_type || 'warehouse', return_date, applicant, itemsJson, total_quantity || calcTotalQty, total_amount || calcTotalAmt, cost_adjustment || 0, refund_amount || 0, remark, attachments]); res.json({ success: true, message: '退库单创建成功', data: { id: (result.rows[0]?.id || result.rows?.[0]?.id), code } }); } 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 returnResult = await db.query('SELECT * FROM return_records WHERE id = $1', [id]); if (returnResult.rows.length === 0) { return res.status(404).json({ success: false, message: '退库单不存在' }); } const returnRecord = returnResult.rows[0]; if (returnRecord.status !== 'pending') { return res.status(400).json({ success: false, message: '只能确认待审核状态的退库单' }); } await db.query('BEGIN TRANSACTION'); try { await db.query("UPDATE return_records SET status = 'confirmed' WHERE id = ?", [id]); if (returnRecord.items) { let items; try { items = JSON.parse(returnRecord.items); } catch (e) { items = []; } for (const item of items) { if (item.quantity > 0 && item.product_id) { const existingInventory = await db.query(` SELECT * FROM project_material_inventory WHERE project_id = ? AND product_id = ? `, [returnRecord.project_id, item.product_id]); if (existingInventory.rows.length > 0) { const existing = existingInventory.rows[0]; const newReturnedQty = (existing.returned_quantity || 0) + item.quantity; const newCurrentQty = Math.max(0, (existing.current_quantity || 0) - item.quantity); const newTotalAmount = Math.max(0, (existing.total_amount || 0) - (item.quantity * item.unit_price || 0)); const newAvgPrice = newCurrentQty > 0 ? newTotalAmount / newCurrentQty : 0; await db.query(` UPDATE project_material_inventory SET returned_quantity = ?, current_quantity = ?, total_amount = ?, average_price = ?, updated_at = CURRENT_TIMESTAMP WHERE project_id = ? AND product_id = ? `, [newReturnedQty, newCurrentQty, newTotalAmount, newAvgPrice, returnRecord.project_id, item.product_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 return_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: '驳回退库失败' }); } }); /** * 更新退库单 */ router.put('/:id', async (req, res) => { try { const { id } = req.params; const { return_type, return_date, items, total_quantity, total_amount, cost_adjustment, refund_amount, remark, attachments } = req.body; const returnResult = await db.query('SELECT * FROM return_records WHERE id = $1', [id]); if (returnResult.rows.length === 0) { return res.status(404).json({ success: false, message: '退库单不存在' }); } const returnRecord = returnResult.rows[0]; if (returnRecord.status !== 'pending') { return res.status(400).json({ success: false, message: '只能修改待审核状态的退库单' }); } const itemsJson = items ? JSON.stringify(items) : null; await db.query(` UPDATE return_records SET return_type = ?, return_date = ?, items = ?, total_quantity = ?, total_amount = ?, cost_adjustment = ?, refund_amount = ?, remark = ?, attachments = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? `, [return_type, return_date, itemsJson, total_quantity, total_amount, cost_adjustment, refund_amount, 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 returnResult = await db.query('SELECT * FROM return_records WHERE id = $1', [id]); if (returnResult.rows.length === 0) { return res.status(404).json({ success: false, message: '退库单不存在' }); } const returnRecord = returnResult.rows[0]; if (returnRecord.status !== 'pending') { return res.status(400).json({ success: false, message: '只能删除待审核状态的退库单' }); } await db.query('DELETE FROM return_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('/project-materials/:projectId', async (req, res) => { try { const { projectId } = req.params; const result = await db.query(` SELECT pmi.*, p.name as product_name, p.specification, p.unit FROM project_material_inventory pmi LEFT JOIN products p ON pmi.product_id = p.id WHERE pmi.project_id = ? AND pmi.current_quantity > 0 ORDER BY p.name `, [projectId]); res.json({ success: true, data: result.rows }); } catch (error) { console.error('获取项目材料列表失败:', error); res.status(500).json({ success: false, message: '获取项目材料列表失败', error: process.env.NODE_ENV === 'development' ? error.message : '操作失败' }); } }); module.exports = router;