/** * 项目材料管理路由 * 遵循设计方案:采购-付款-物流-退库一体化流程设计方案.md * 章节:九、项目材料管理 * * 功能: * - 材料库存:显示项目当前材料库存 * - 采购记录:显示项目关联的所有采购订单 * - 退库记录:显示项目材料退库记录 * - 材料价格历史:查询材料的历史采购价格 */ const express = require('express'); const db = require('../db'); const router = express.Router(); /** * 获取项目材料库存列表 */ router.get('/inventory/:projectId', async (req, res) => { try { const { projectId } = req.params; const result = await db.query(` SELECT pmi.*, p.name as product_name, p.specification FROM project_material_inventory pmi LEFT JOIN products p ON pmi.product_id = p.id WHERE pmi.project_id = ? ORDER BY p.name `, [projectId]); 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('/inventory/:projectId/summary', async (req, res) => { try { const { projectId } = req.params; const result = await db.query(` SELECT COUNT(*) as item_count, SUM(purchased_quantity) as total_purchased, SUM(received_quantity) as total_received, SUM(used_quantity) as total_used, SUM(returned_quantity) as total_returned, SUM(current_quantity) as total_current, SUM(total_amount) as total_value FROM project_material_inventory WHERE project_id = ? `, [projectId]); res.json({ success: true, data: result.rows[0] }); } catch (error) { console.error('获取项目材料库存汇总失败:', error); res.status(500).json({ success: false, message: '获取项目材料库存汇总失败', error: error.message }); } }); /** * 获取项目采购记录 */ router.get('/purchases/:projectId', async (req, res) => { try { const { projectId } = req.params; const { status } = req.query; let query = ` SELECT po.*, s.name as supplier_name, (SELECT SUM(total_price) FROM purchase_order_items WHERE order_id = po.id) as total_amount, (SELECT SUM(CASE WHEN pp.status = 'paid' THEN COALESCE(pp.actual_amount, 0) ELSE 0 END) FROM payment_plans pp WHERE pp.purchase_order_id = po.id) as paid_amount FROM purchase_orders po LEFT JOIN suppliers s ON po.supplier_id = s.id WHERE po.project_id = ? `; const params = [projectId]; if (status) { query += ' AND po.status = $1'; params.push(status); } query += ' ORDER BY po.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('/returns/:projectId', async (req, res) => { try { const { projectId } = req.params; const { status } = req.query; let query = ` SELECT * FROM return_records WHERE project_id = ? `; const params = [projectId]; if (status) { query += ' AND status = $1'; params.push(status); } query += ' ORDER BY created_at DESC'; const result = await db.query(query, params); const processedResults = result.rows.map(row => { if (row.items) { try { row.items = JSON.parse(row.items); } catch (e) { row.items = []; } } return row; }); res.json({ success: true, data: processedResults, count: processedResults.length }); } catch (error) { console.error('获取项目退库记录失败:', error); res.status(500).json({ success: false, message: '获取项目退库记录失败', error: error.message }); } }); /** * 获取材料价格历史 * 遵循设计方案:每次订单确认时自动写入material_price_history表 */ router.get('/price-history/:productId', async (req, res) => { try { const { productId } = req.params; const { supplier_id, limit } = req.query; let query = ` SELECT mph.*, s.name as supplier_name, s.country as supplier_country, po.code as order_code FROM material_price_history mph LEFT JOIN suppliers s ON mph.supplier_id = s.id LEFT JOIN purchase_orders po ON mph.purchase_order_id = po.id WHERE mph.product_id = ? `; const params = [productId]; if (supplier_id) { query += ' AND mph.supplier_id = $1'; params.push(supplier_id); } query += ' ORDER BY mph.purchase_date DESC'; if (limit) { query += ' LIMIT $1'; params.push(parseInt(limit)); } 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('/average-price/:productId', async (req, res) => { try { const { productId } = req.params; const result = await db.query(` SELECT AVG(unit_price) as avg_price, MIN(unit_price) as min_price, MAX(unit_price) as max_price, COUNT(*) as purchase_count, SUM(quantity) as total_quantity FROM material_price_history WHERE product_id = ? `, [productId]); res.json({ success: true, data: result.rows[0] }); } catch (error) { console.error('获取材料平均价格失败:', error); res.status(500).json({ success: false, message: '获取材料平均价格失败', error: error.message }); } }); /** * 获取材料近期价格趋势 */ router.get('/price-trend/:productId', async (req, res) => { try { const { productId } = req.params; const { months } = req.query; const monthLimit = parseInt(months) || 6; const result = await db.query(` SELECT strftime('%Y-%m', purchase_date) as month, AVG(unit_price) as avg_price, SUM(quantity) as total_quantity, COUNT(*) as purchase_count FROM material_price_history WHERE product_id = ? AND purchase_date >= date('now', '-' || ? || ' months') GROUP BY strftime('%Y-%m', purchase_date) ORDER BY month DESC `, [productId, monthLimit]); res.json({ success: true, data: result.rows }); } catch (error) { console.error('获取材料价格趋势失败:', error); res.status(500).json({ success: false, message: '获取材料价格趋势失败', error: error.message }); } }); /** * 更新项目材料库存(手动调整) */ router.put('/inventory/:projectId/:productId', async (req, res) => { try { const { projectId, productId } = req.params; const { used_quantity, remark } = req.body; const existingResult = await db.query(` SELECT * FROM project_material_inventory WHERE project_id = ? AND product_id = ? `, [projectId, productId]); if (existingResult.rows.length === 0) { return res.status(404).json({ success: false, message: '材料库存记录不存在' }); } const existing = existingResult.rows[0]; const newUsedQty = (existing.used_quantity || 0) + (used_quantity || 0); const newCurrentQty = Math.max(0, (existing.current_quantity || 0) - (used_quantity || 0)); await db.query(` UPDATE project_material_inventory SET used_quantity = ?, current_quantity = ?, updated_at = CURRENT_TIMESTAMP WHERE project_id = ? AND product_id = ? `, [newUsedQty, newCurrentQty, projectId, productId]); res.json({ success: true, message: '库存更新成功', data: { used_quantity: newUsedQty, current_quantity: newCurrentQty } }); } catch (error) { console.error('更新材料库存失败:', error); res.status(500).json({ success: false, message: '更新材料库存失败', error: error.message }); } }); /** * 获取所有项目的材料库存汇总 */ router.get('/all-projects-summary', async (req, res) => { try { const result = await db.query(` SELECT p.id as project_id, p.name as project_name, COUNT(pmi.id) as item_count, SUM(pmi.current_quantity) as total_quantity, SUM(pmi.total_amount) as total_value FROM projects p LEFT JOIN project_material_inventory pmi ON p.id = pmi.project_id WHERE p.status = 'active' GROUP BY p.id ORDER BY p.name `); res.json({ success: true, data: result.rows }); } catch (error) { console.error('获取所有项目材料汇总失败:', error); res.status(500).json({ success: false, message: '获取所有项目材料汇总失败', error: error.message }); } }); module.exports = router;