Files
yunhaifinance/backend/routes/project-materials.js
T

365 lines
9.5 KiB
JavaScript

/**
* 项目材料管理路由
* 遵循设计方案:采购-付款-物流-退库一体化流程设计方案.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;