467 lines
12 KiB
JavaScript
467 lines
12 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const db = require('./db');
|
|
|
|
// 获取所有付款节点
|
|
router.get('/payment-nodes', async (req, res) => {
|
|
try {
|
|
const result = await db.query(`
|
|
SELECT
|
|
pn.*,
|
|
pr.project_name,
|
|
pr.project_code
|
|
FROM payment_nodes pn
|
|
LEFT JOIN projects pr ON pn.project_id = pr.project_id
|
|
ORDER BY pn.due_date ASC
|
|
`);
|
|
|
|
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('/payment-nodes/:id', async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const result = await db.query(`
|
|
SELECT
|
|
pn.*,
|
|
pr.project_name,
|
|
pr.project_code
|
|
FROM payment_nodes pn
|
|
LEFT JOIN projects pr ON pn.project_id = pr.project_id
|
|
WHERE pn.node_id = $1
|
|
`, [id]);
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({
|
|
success: false,
|
|
message: '付款节点不存在'
|
|
});
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.rows[0]
|
|
});
|
|
} catch (error) {
|
|
console.error('获取付款节点失败:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: '获取付款节点失败',
|
|
error: error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
// 创建付款节点
|
|
router.post('/payment-nodes', async (req, res) => {
|
|
try {
|
|
const {
|
|
project_id,
|
|
node_type,
|
|
node_name,
|
|
node_name_zh,
|
|
node_name_th,
|
|
node_name_en,
|
|
amount,
|
|
currency,
|
|
due_date,
|
|
status,
|
|
notes
|
|
} = req.body;
|
|
|
|
const result = await db.query(`
|
|
INSERT INTO payment_nodes (
|
|
project_id, node_type, node_name,
|
|
node_name_zh, node_name_th, node_name_en,
|
|
amount, currency, due_date, status, notes
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
RETURNING *
|
|
`, [
|
|
project_id, node_type, node_name,
|
|
node_name_zh, node_name_th, node_name_en,
|
|
amount, currency, due_date, status || 'pending', notes
|
|
]);
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
message: '付款节点创建成功',
|
|
data: result.rows[0]
|
|
});
|
|
} catch (error) {
|
|
console.error('创建付款节点失败:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: '创建付款节点失败',
|
|
error: error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
// 更新付款节点
|
|
router.put('/payment-nodes/:id', async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const {
|
|
node_type,
|
|
node_name,
|
|
node_name_zh,
|
|
node_name_th,
|
|
node_name_en,
|
|
amount,
|
|
currency,
|
|
due_date,
|
|
status,
|
|
notes
|
|
} = req.body;
|
|
|
|
const result = await db.query(`
|
|
UPDATE payment_nodes
|
|
SET
|
|
node_type = COALESCE($1, node_type),
|
|
node_name = COALESCE($2, node_name),
|
|
node_name_zh = COALESCE($3, node_name_zh),
|
|
node_name_th = COALESCE($4, node_name_th),
|
|
node_name_en = COALESCE($5, node_name_en),
|
|
amount = COALESCE($6, amount),
|
|
currency = COALESCE($7, currency),
|
|
due_date = COALESCE($8, due_date),
|
|
status = COALESCE($9, status),
|
|
notes = COALESCE($10, notes),
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE node_id = $11
|
|
RETURNING *
|
|
`, [
|
|
node_type, node_name, node_name_zh, node_name_th, node_name_en,
|
|
amount, currency, due_date, status, notes, id
|
|
]);
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({
|
|
success: false,
|
|
message: '付款节点不存在'
|
|
});
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
message: '付款节点更新成功',
|
|
data: result.rows[0]
|
|
});
|
|
} catch (error) {
|
|
console.error('更新付款节点失败:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: '更新付款节点失败',
|
|
error: error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
// 删除付款节点
|
|
router.delete('/payment-nodes/:id', async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
const result = await db.query(
|
|
'DELETE FROM payment_nodes WHERE node_id = $1 RETURNING *',
|
|
[id]
|
|
);
|
|
|
|
if (result.rows.length === 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('/payment-records', async (req, res) => {
|
|
try {
|
|
const { node_id, record_type, start_date, end_date } = req.query;
|
|
|
|
let query = `
|
|
SELECT
|
|
pr.*,
|
|
pn.node_name,
|
|
pn.project_id,
|
|
proj.project_name
|
|
FROM payment_records pr
|
|
LEFT JOIN payment_nodes pn ON pr.node_id = pn.node_id
|
|
LEFT JOIN projects proj ON pn.project_id = proj.project_id
|
|
WHERE 1=1
|
|
`;
|
|
const params = [];
|
|
let paramIndex = 1;
|
|
|
|
if (node_id) {
|
|
query += ` AND pr.node_id = $${paramIndex}`;
|
|
params.push(node_id);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (record_type) {
|
|
query += ` AND pr.record_type = $${paramIndex}`;
|
|
params.push(record_type);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (start_date) {
|
|
query += ` AND pr.payment_date >= $${paramIndex}`;
|
|
params.push(start_date);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (end_date) {
|
|
query += ` AND pr.payment_date <= $${paramIndex}`;
|
|
params.push(end_date);
|
|
paramIndex++;
|
|
}
|
|
|
|
query += ` ORDER BY pr.payment_date DESC, pr.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.post('/payment-records', async (req, res) => {
|
|
try {
|
|
const {
|
|
node_id,
|
|
record_type,
|
|
amount,
|
|
currency,
|
|
exchange_rate,
|
|
payment_date,
|
|
payment_method,
|
|
reference_number,
|
|
status,
|
|
notes
|
|
} = req.body;
|
|
|
|
const result = await db.query(`
|
|
INSERT INTO payment_records (
|
|
node_id, record_type, amount, currency, exchange_rate,
|
|
payment_date, payment_method, reference_number, status, notes
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
RETURNING *
|
|
`, [
|
|
node_id, record_type, amount, currency, exchange_rate || 1.0,
|
|
payment_date, payment_method, reference_number, status || 'completed', notes
|
|
]);
|
|
|
|
// 如果是付款记录,更新付款节点状态
|
|
if (record_type === 'payment') {
|
|
await db.query(`
|
|
UPDATE payment_nodes
|
|
SET status = 'paid', updated_at = CURRENT_TIMESTAMP
|
|
WHERE node_id = $1
|
|
`, [node_id]);
|
|
}
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
message: '付款记录创建成功',
|
|
data: result.rows[0]
|
|
});
|
|
} catch (error) {
|
|
console.error('创建付款记录失败:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: '创建付款记录失败',
|
|
error: error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
// 获取汇率
|
|
router.get('/exchange-rates', async (req, res) => {
|
|
try {
|
|
const { from_currency, to_currency, effective_date } = req.query;
|
|
|
|
let query = 'SELECT * FROM exchange_rates WHERE 1=1';
|
|
const params = [];
|
|
let paramIndex = 1;
|
|
|
|
if (from_currency) {
|
|
query += ` AND from_currency = $${paramIndex}`;
|
|
params.push(from_currency);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (to_currency) {
|
|
query += ` AND to_currency = $${paramIndex}`;
|
|
params.push(to_currency);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (effective_date) {
|
|
query += ` AND effective_date = $${paramIndex}`;
|
|
params.push(effective_date);
|
|
paramIndex++;
|
|
}
|
|
|
|
query += ` ORDER BY effective_date DESC, 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.post('/exchange-rates', async (req, res) => {
|
|
try {
|
|
const { from_currency, to_currency, rate, effective_date } = req.body;
|
|
|
|
// 检查是否已存在
|
|
const checkResult = await db.query(`
|
|
SELECT * FROM exchange_rates
|
|
WHERE from_currency = $1 AND to_currency = $2 AND effective_date = $3
|
|
`, [from_currency, to_currency, effective_date]);
|
|
|
|
let result;
|
|
if (checkResult.rows.length > 0) {
|
|
// 更新
|
|
result = await db.query(`
|
|
UPDATE exchange_rates
|
|
SET rate = $1, updated_at = CURRENT_TIMESTAMP
|
|
WHERE from_currency = $2 AND to_currency = $3 AND effective_date = $4
|
|
RETURNING *
|
|
`, [rate, from_currency, to_currency, effective_date]);
|
|
} else {
|
|
// 创建
|
|
result = await db.query(`
|
|
INSERT INTO exchange_rates (from_currency, to_currency, rate, effective_date)
|
|
VALUES ($1, $2, $3, $4)
|
|
RETURNING *
|
|
`, [from_currency, to_currency, rate, effective_date]);
|
|
}
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
message: '汇率保存成功',
|
|
data: result.rows[0]
|
|
});
|
|
} catch (error) {
|
|
console.error('保存汇率失败:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: '保存汇率失败',
|
|
error: error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
// 财务统计
|
|
router.get('/finance-stats', async (req, res) => {
|
|
try {
|
|
// 付款节点统计
|
|
const nodesStats = await db.query(`
|
|
SELECT
|
|
COUNT(*) as total_nodes,
|
|
COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending_nodes,
|
|
COUNT(CASE WHEN status = 'paid' THEN 1 END) as paid_nodes,
|
|
COUNT(CASE WHEN status = 'overdue' THEN 1 END) as overdue_nodes,
|
|
SUM(amount) as total_amount,
|
|
SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) as pending_amount,
|
|
SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) as paid_amount
|
|
FROM payment_nodes
|
|
`);
|
|
|
|
// 付款记录统计
|
|
const recordsStats = await db.query(`
|
|
SELECT
|
|
COUNT(*) as total_records,
|
|
COUNT(CASE WHEN record_type = 'payment' THEN 1 END) as payment_count,
|
|
COUNT(CASE WHEN record_type = 'receipt' THEN 1 END) as receipt_count,
|
|
SUM(CASE WHEN record_type = 'payment' THEN amount ELSE 0 END) as total_payments,
|
|
SUM(CASE WHEN record_type = 'receipt' THEN amount ELSE 0 END) as total_receipts
|
|
FROM payment_records
|
|
`);
|
|
|
|
// 货币分布
|
|
const currencyStats = await db.query(`
|
|
SELECT
|
|
currency,
|
|
COUNT(*) as node_count,
|
|
SUM(amount) as total_amount
|
|
FROM payment_nodes
|
|
GROUP BY currency
|
|
ORDER BY total_amount DESC
|
|
`);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
nodes: nodesStats.rows[0],
|
|
records: recordsStats.rows[0],
|
|
currencies: currencyStats.rows,
|
|
summary: {
|
|
net_cash_flow: (recordsStats.rows[0]?.total_receipts || 0) - (recordsStats.rows[0]?.total_payments || 0),
|
|
outstanding_amount: nodesStats.rows[0]?.pending_amount || 0
|
|
}
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('获取财务统计失败:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: '获取财务统计失败',
|
|
error: error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
module.exports = router; |