Files
yunhaifinance/backend/routes/purchase-orders.js
T

636 lines
19 KiB
JavaScript

/**
* 采购订单路由
* 遵循设计方案:采购-付款-物流-退库一体化流程设计方案.md
* 章节:三、采购订单页面设计
*
* 多TAB设计:
* - 基本信息:订单基本信息、供应商选择
* - 商品明细:订单商品列表
* - 付款信息:付款计划列表
* - 物流信息:物流单信息
* - 验收记录:验收单列表
*/
const express = require('express');
const db = require('../db');
const router = express.Router();
/**
* 获取采购订单列表
* 遵循设计方案:支持按项目、供应商、状态筛选
*/
router.get('/', async (req, res) => {
try {
const { project_id, supplier_id, status } = req.query;
let query = `
SELECT po.*,
p.name as project_name,
s.name as supplier_name,
(SELECT SUM(planned_amount) FROM payment_plans WHERE purchase_order_id = po.id AND status = 'paid') as paid_amount
FROM purchase_orders po
LEFT JOIN projects p ON po.project_id = p.id
LEFT JOIN suppliers s ON po.supplier_id = s.id
`;
const params = [];
const conditions = [];
if (project_id) {
conditions.push('po.project_id = $1');
params.push(project_id);
}
if (supplier_id) {
conditions.push('po.supplier_id = $1');
params.push(supplier_id);
}
if (status) {
conditions.push('po.status = $1');
params.push(status);
}
if (conditions.length > 0) {
query += ' WHERE ' + conditions.join(' AND ');
}
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
});
}
});
/**
* 获取采购订单详情(包含所有TAB数据)
* 遵循设计方案:三、采购订单页面设计 - 多TAB
*/
router.get('/:id', async (req, res) => {
try {
const { id } = req.params;
const orderResult = await db.query(`
SELECT po.*,
p.name as project_name,
s.name as supplier_name,
s.country as supplier_country
FROM purchase_orders po
LEFT JOIN projects p ON po.project_id = p.id
LEFT JOIN suppliers s ON po.supplier_id = s.id
WHERE po.id = ?
`, [id]);
if (orderResult.rows.length === 0) {
return res.status(404).json({ success: false, message: '采购订单不存在' });
}
const order = orderResult.rows[0];
const itemsResult = await db.query(`
SELECT poi.*, p.name as product_name, p.specification, p.unit
FROM purchase_order_items poi
LEFT JOIN products p ON poi.product_id = p.id
WHERE poi.purchase_order_id = ?
ORDER BY poi.id
`, [id]);
order.items = itemsResult.rows;
const paymentPlansResult = await db.query(`
SELECT pp.*, pr.status as request_status
FROM payment_plans pp
LEFT JOIN payment_requests pr ON pp.payment_request_id = pr.id
WHERE pp.purchase_order_id = ?
ORDER BY pp.stage, pp.id
`, [id]);
order.payment_plans = paymentPlansResult.rows;
const logisticsResult = await db.query(`
SELECT lr.*, lc.name as logistics_company_name
FROM logistics_records lr
LEFT JOIN logistics_companies lc ON lr.logistics_company_id = lc.id
WHERE lr.purchase_order_id = ?
ORDER BY lr.id DESC
`, [id]);
order.logistics = logisticsResult.rows;
const verificationResult = await db.query(`
SELECT vr.*, p.name as project_name
FROM verification_records vr
LEFT JOIN projects p ON vr.project_id = p.id
WHERE vr.purchase_order_id = ?
ORDER BY vr.id DESC
`, [id]);
order.verifications = verificationResult.rows;
const totalAmountResult = await db.query(
'SELECT SUM(total_price) as total FROM purchase_order_items WHERE purchase_order_id = $1',
[id]
);
order.total_amount = totalAmountResult.rows[0]?.total || 0;
const paidAmountResult = await db.query(
"SELECT SUM(actual_amount) as paid FROM payment_plans WHERE purchase_order_id = $1 AND status = 'paid'",
[id]
);
order.paid_amount = paidAmountResult.rows[0]?.paid || 0;
res.json({
success: true,
data: order
});
} catch (error) {
console.error('获取采购订单详情失败:', error);
res.status(500).json({
success: false,
message: '获取采购订单详情失败',
error: error.message
});
}
});
/**
* 创建采购订单
*/
router.post('/', async (req, res) => {
try {
const { code, purchase_request_id, project_id, supplier_id, currency, remark, created_by } = req.body;
const orderCode = code || 'PO' + new Date().toISOString().slice(0, 10).replace(/-/g, '') +
String(Math.floor(Math.random() * 10000)).padStart(4, '0');
const result = await db.query(`
INSERT INTO purchase_orders
(code, purchase_request_id, project_id, supplier_id, currency, status, remark, created_by, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, 'draft', ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
`, [orderCode, purchase_request_id, project_id, supplier_id, currency || 'CNY', remark, created_by]);
res.json({
success: true,
message: '采购订单创建成功',
data: { id: (result.rows[0]?.id || result.rows?.[0]?.id), code: orderCode }
});
} 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 { supplier_id, supplier_country, contract_url, quotation_url, remark } = req.body;
const result = await db.query(`
UPDATE purchase_orders
SET supplier_id = ?, supplier_country = ?, contract_url = ?, quotation_url = ?, remark = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`, [supplier_id, supplier_country, contract_url, quotation_url, remark, 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.post('/:id/confirm', async (req, res) => {
try {
const { id } = req.params;
const { payment_stages } = req.body;
const orderResult = await db.query('SELECT * FROM purchase_orders WHERE id = $1', [id]);
if (orderResult.rows.length === 0) {
return res.status(404).json({ success: false, message: '采购订单不存在' });
}
const order = orderResult.rows[0];
const itemsResult = await db.query('SELECT * FROM purchase_order_items WHERE purchase_order_id = $1', [id]);
const items = itemsResult.rows;
if (items.length === 0) {
return res.status(400).json({ success: false, message: '订单没有商品明细,无法确认' });
}
await db.query('BEGIN TRANSACTION');
try {
await db.query("UPDATE purchase_orders SET status = 'confirmed', updated_at = CURRENT_TIMESTAMP WHERE id = ?", [id]);
const totalAmount = items.reduce((sum, item) => sum + (item.total_price || 0), 0);
await db.query('UPDATE purchase_orders SET total_amount = $1 WHERE id = $2', [totalAmount, id]);
for (const item of items) {
await db.query(`
INSERT INTO material_price_history
(product_id, purchase_order_id, supplier_id, supplier_country, unit_price, currency, quantity, purchase_date, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_DATE, CURRENT_TIMESTAMP)
`, [item.product_id, id, order.supplier_id, order.supplier_country, item.unit_price, order.currency, item.quantity]);
}
if (payment_stages && payment_stages.length > 0) {
for (const stage of payment_stages) {
await db.query(`
INSERT INTO payment_plans
(purchase_order_id, stage, planned_date, planned_amount, planned_percentage, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, 'pending', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
`, [id, stage.name, stage.date, stage.amount, stage.percentage]);
}
}
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/cancel', async (req, res) => {
try {
const { id } = req.params;
const result = await db.query(
"UPDATE purchase_orders SET status = 'cancelled', updated_at = CURRENT_TIMESTAMP WHERE id = ?",
[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.get('/:id/items', async (req, res) => {
try {
const { id } = req.params;
const result = await db.query(`
SELECT poi.*, p.name as product_name, p.specification, p.unit
FROM purchase_order_items poi
LEFT JOIN products p ON poi.product_id = p.id
WHERE poi.purchase_order_id = ?
ORDER BY poi.id
`, [id]);
res.json({
success: true,
data: result.rows
});
} catch (error) {
console.error('获取订单商品明细失败:', error);
res.status(500).json({
success: false,
message: '获取订单商品明细失败',
error: error.message
});
}
});
/**
* 添加订单商品明细
*/
router.post('/:id/items', async (req, res) => {
try {
const { id } = req.params;
const { product_id, product_name, specification, unit, quantity, unit_price } = req.body;
const total_price = (quantity || 0) * (unit_price || 0);
const result = await db.query(`
INSERT INTO purchase_order_items
(purchase_order_id, product_id, product_name, specification, unit, quantity, unit_price, total_price, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
`, [id, product_id, product_name, specification, unit, quantity, unit_price, total_price]);
res.json({
success: true,
message: '商品明细添加成功',
data: { id: (result.rows[0]?.id || result.rows?.[0]?.id) }
});
} catch (error) {
console.error('添加商品明细失败:', error);
res.status(500).json({
success: false,
message: '添加商品明细失败',
error: error.message
});
}
});
/**
* 更新订单商品明细
*/
router.put('/:id/items/:itemId', async (req, res) => {
try {
const { id, itemId } = req.params;
const { product_id, product_name, specification, unit, quantity, unit_price } = req.body;
const total_price = (quantity || 0) * (unit_price || 0);
const result = await db.query(`
UPDATE purchase_order_items
SET product_id = ?, product_name = ?, specification = ?, unit = ?, quantity = ?, unit_price = ?, total_price = ?
WHERE id = ? AND purchase_order_id = ?
`, [product_id, product_name, specification, unit, quantity, unit_price, total_price, itemId, 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.delete('/:id/items/:itemId', async (req, res) => {
try {
const { id, itemId } = req.params;
const result = await db.query(
'DELETE FROM purchase_order_items WHERE id = $1 AND purchase_order_id = $2',
[itemId, 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.get('/:id/payment-plans', async (req, res) => {
try {
const { id } = req.params;
const result = await db.query(`
SELECT pp.*, pr.status as request_status
FROM payment_plans pp
LEFT JOIN payment_requests pr ON pp.payment_request_id = pr.id
WHERE pp.purchase_order_id = ?
ORDER BY pp.stage, pp.id
`, [id]);
res.json({
success: true,
data: result.rows
});
} catch (error) {
console.error('获取付款计划失败:', error);
res.status(500).json({
success: false,
message: '获取付款计划失败',
error: error.message
});
}
});
/**
* 添加付款计划
*/
router.post('/:id/payment-plans', async (req, res) => {
try {
const { id } = req.params;
const { stage, planned_date, planned_amount, planned_percentage, remark } = req.body;
const result = await db.query(`
INSERT INTO payment_plans
(purchase_order_id, stage, planned_date, planned_amount, planned_percentage, remark, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, 'pending', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
`, [id, stage, planned_date, planned_amount, planned_percentage, remark]);
res.json({
success: true,
message: '付款计划添加成功',
data: { id: (result.rows[0]?.id || result.rows?.[0]?.id) }
});
} catch (error) {
console.error('添加付款计划失败:', error);
res.status(500).json({
success: false,
message: '添加付款计划失败',
error: error.message
});
}
});
/**
* 更新付款计划
*/
router.put('/:id/payment-plans/:planId', async (req, res) => {
try {
const { id, planId } = req.params;
const { stage, planned_date, planned_amount, planned_percentage, remark } = req.body;
const result = await db.query(`
UPDATE payment_plans
SET stage = $1, planned_date = $2, planned_amount = $3, planned_percentage = $4, remark = $5, updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND purchase_order_id = ?
`, [stage, planned_date, planned_amount, planned_percentage, remark, planId, 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.delete('/:id/payment-plans/:planId', async (req, res) => {
try {
const { id, planId } = req.params;
const result = await db.query(
'DELETE FROM payment_plans WHERE id = $1 AND purchase_order_id = $2',
[planId, 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.get('/:id/logistics', async (req, res) => {
try {
const { id } = req.params;
const result = await db.query(`
SELECT lr.*, lc.name as logistics_company_name
FROM logistics_records lr
LEFT JOIN logistics_companies lc ON lr.logistics_company_id = lc.id
WHERE lr.purchase_order_id = ?
ORDER BY lr.id DESC
`, [id]);
res.json({
success: true,
data: result.rows
});
} catch (error) {
console.error('获取物流信息失败:', error);
res.status(500).json({
success: false,
message: '获取物流信息失败',
error: error.message
});
}
});
/**
* 获取订单验收记录
*/
router.get('/:id/verifications', async (req, res) => {
try {
const { id } = req.params;
const result = await db.query(`
SELECT vr.*, p.name as project_name
FROM verification_records vr
LEFT JOIN projects p ON vr.project_id = p.id
WHERE vr.purchase_order_id = ?
ORDER BY vr.id DESC
`, [id]);
res.json({
success: true,
data: result.rows
});
} 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;
await db.query('BEGIN TRANSACTION');
try {
await db.query('DELETE FROM purchase_order_items WHERE purchase_order_id = $1', [id]);
await db.query('DELETE FROM payment_plans WHERE purchase_order_id = $1', [id]);
await db.query('DELETE FROM purchase_orders WHERE id = $1', [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
});
}
});
module.exports = router;