365 lines
13 KiB
JavaScript
365 lines
13 KiB
JavaScript
const express = require('express');
|
|||
|
|
const db = require('../db');
|
||
|
|
const { authenticate, requireAdmin } = require('../middleware/auth');
|
||
|
|
|
||
|
|
const router = express.Router();
|
||
|
|
|
||
|
|
router.get('/', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { advance_id } = req.query;
|
||
|
|
let query = `
|
||
|
|
SELECT v.*, a.advance_code, a.applicant_id as advance_applicant_id
|
||
|
|
FROM verifications v
|
||
|
|
LEFT JOIN advances a ON v.advance_id = a.id
|
||
|
|
`;
|
||
|
|
const params = [];
|
||
|
|
|
||
|
|
if (advance_id) {
|
||
|
|
query += ` WHERE v.advance_id = $1`;
|
||
|
|
params.push(advance_id);
|
||
|
|
}
|
||
|
|
|
||
|
|
query += ` ORDER BY v.created_at DESC`;
|
||
|
|
|
||
|
|
const result = await db.query(query, params);
|
||
|
|
|
||
|
|
const data = result.rows.map(item => {
|
||
|
|
if (item.attachments) {
|
||
|
|
try {
|
||
|
|
item.attachments = JSON.parse(item.attachments);
|
||
|
|
} catch (error) {
|
||
|
|
item.attachments = [];
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
item.attachments = [];
|
||
|
|
}
|
||
|
|
if (item.detail_items) {
|
||
|
|
try {
|
||
|
|
item.detail_items = JSON.parse(item.detail_items);
|
||
|
|
} catch (error) {
|
||
|
|
item.detail_items = [];
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
item.detail_items = [];
|
||
|
|
}
|
||
|
|
return item;
|
||
|
|
});
|
||
|
|
|
||
|
|
res.json({ success: true, data, count: data.length });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('获取核销记录失败:', error);
|
||
|
|
res.status(500).json({ success: false, message: '获取核销记录失败', error: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
router.post('/', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { verification_date, advance_id, advance_code, advance_amount, currency, reason, detail_items, attachments, applicant, expense_type, project_id, settlement, settlement_amount } = req.body;
|
||
|
|
|
||
|
|
// 生成核销编号
|
||
|
|
const verificationCode = `VER-${Date.now()}`;
|
||
|
|
const amount = detail_items?.reduce((sum, item) => sum + (item.amount || 0), 0) || 0;
|
||
|
|
|
||
|
|
// 验证关联预支单
|
||
|
|
if (!advance_id && !advance_code) {
|
||
|
|
return res.status(400).json({ success: false, message: '关联预支单是必填项' });
|
||
|
|
}
|
||
|
|
|
||
|
|
let finalAdvanceCode = advance_code;
|
||
|
|
let finalAdvanceId = advance_id;
|
||
|
|
|
||
|
|
// 如果advance_code为空,根据advance_id查询预支单的advance_code
|
||
|
|
if (!finalAdvanceCode && finalAdvanceId) {
|
||
|
|
const advanceResult = await db.query('SELECT advance_code FROM advances WHERE id = $1', [finalAdvanceId]);
|
||
|
|
if (advanceResult.rows.length > 0) {
|
||
|
|
finalAdvanceCode = advanceResult.rows[0].advance_code;
|
||
|
|
} else {
|
||
|
|
return res.status(400).json({ success: false, message: '关联的预支单不存在' });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 如果advance_id为空,根据advance_code查询预支单的id
|
||
|
|
if (!finalAdvanceId && finalAdvanceCode) {
|
||
|
|
const advanceResult = await db.query('SELECT id FROM advances WHERE advance_code = $1', [finalAdvanceCode]);
|
||
|
|
if (advanceResult.rows.length > 0) {
|
||
|
|
finalAdvanceId = advanceResult.rows[0].id;
|
||
|
|
} else {
|
||
|
|
return res.status(400).json({ success: false, message: '关联的预支单不存在' });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 如果仍然为空,返回错误
|
||
|
|
if (!finalAdvanceCode || !finalAdvanceId) {
|
||
|
|
return res.status(400).json({ success: false, message: '关联预支单不存在' });
|
||
|
|
}
|
||
|
|
|
||
|
|
// 开始事务
|
||
|
|
await db.query('BEGIN TRANSACTION');
|
||
|
|
|
||
|
|
try {
|
||
|
|
// 插入核销申请
|
||
|
|
const result = await db.query(
|
||
|
|
'INSERT INTO verifications (advance_id, amount, currency, reason, verification_date, verification_code, status, applicant, advance_code, advance_amount, detail_items, attachments, expense_type, project_id, settlement, settlement_amount) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)',
|
||
|
|
[finalAdvanceId, amount, currency || 'CNY', reason, verification_date, verificationCode, 'pending', applicant, finalAdvanceCode, advance_amount || 0, JSON.stringify(detail_items || []), JSON.stringify(attachments || []), expense_type || 'company', project_id, settlement ? 1 : 0, settlement_amount || 0]
|
||
|
|
);
|
||
|
|
|
||
|
|
// 提交事务
|
||
|
|
await db.query('COMMIT');
|
||
|
|
|
||
|
|
// SQLite不支持RETURNING,所以需要查询刚插入的数据
|
||
|
|
const lastInsert = await db.query('SELECT * FROM verifications ORDER BY id DESC LIMIT 1');
|
||
|
|
res.json({ success: true, data: lastInsert.rows[0] });
|
||
|
|
} catch (error) {
|
||
|
|
// 回滚事务
|
||
|
|
await db.query('ROLLBACK');
|
||
|
|
throw error;
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('创建核销申请失败:', error);
|
||
|
|
res.status(500).json({ success: false, message: '创建核销申请失败', error: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
router.get('/:id', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { id } = req.params;
|
||
|
|
|
||
|
|
const result = await db.query('SELECT * FROM verifications WHERE id = $1', [id]);
|
||
|
|
|
||
|
|
if (result.rows.length > 0) {
|
||
|
|
const data = result.rows[0];
|
||
|
|
if (data.attachments) {
|
||
|
|
try {
|
||
|
|
data.attachments = JSON.parse(data.attachments);
|
||
|
|
} catch (error) {
|
||
|
|
data.attachments = [];
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
data.attachments = [];
|
||
|
|
}
|
||
|
|
if (data.detail_items) {
|
||
|
|
try {
|
||
|
|
data.detail_items = JSON.parse(data.detail_items);
|
||
|
|
} catch (error) {
|
||
|
|
data.detail_items = [];
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
data.detail_items = [];
|
||
|
|
}
|
||
|
|
res.json({ success: true, data });
|
||
|
|
} else {
|
||
|
|
res.status(404).json({ success: false, message: '核销申请不存在' });
|
||
|
|
}
|
||
|
|
} 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 { verification_date, advance_id, advance_code, advance_amount, currency, reason, detail_items, attachments, applicant, status, expense_type, project_id, settlement, settlement_amount } = req.body;
|
||
|
|
const amount = detail_items?.reduce((sum, item) => sum + (item.amount || 0), 0) || 0;
|
||
|
|
|
||
|
|
// 开始事务
|
||
|
|
await db.query('BEGIN TRANSACTION');
|
||
|
|
|
||
|
|
try {
|
||
|
|
// 获取原核销金额
|
||
|
|
const oldVerification = await db.query('SELECT amount, advance_id FROM verifications WHERE id = $1', [id]);
|
||
|
|
const oldAmount = oldVerification.rows[0]?.amount || 0;
|
||
|
|
const oldAdvanceId = oldVerification.rows[0]?.advance_id;
|
||
|
|
|
||
|
|
let finalAdvanceCode = advance_code;
|
||
|
|
|
||
|
|
// 如果advance_code为空,根据advance_id查询预支单的advance_code
|
||
|
|
if (!finalAdvanceCode && advance_id) {
|
||
|
|
const advanceResult = await db.query('SELECT advance_code FROM advances WHERE id = $1', [advance_id]);
|
||
|
|
if (advanceResult.rows.length > 0) {
|
||
|
|
finalAdvanceCode = advanceResult.rows[0].advance_code;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 如果仍然为空,使用默认值
|
||
|
|
if (!finalAdvanceCode) {
|
||
|
|
finalAdvanceCode = 'UNKNOWN';
|
||
|
|
}
|
||
|
|
|
||
|
|
// 更新核销申请
|
||
|
|
const result = await db.query(
|
||
|
|
'UPDATE verifications SET verification_date = $1, advance_id = $2, amount = $3, currency = $4, reason = $5, advance_code = $6, advance_amount = $7, detail_items = $8, attachments = $9, applicant = $10, status = $11, expense_type = $12, project_id = $13, settlement = $14, settlement_amount = $15 WHERE id = $16',
|
||
|
|
[verification_date, advance_id, amount, currency, reason, finalAdvanceCode, advance_amount || 0, JSON.stringify(detail_items || []), JSON.stringify(attachments || []), applicant, status, expense_type || 'company', project_id, settlement ? 1 : 0, settlement_amount || 0, id]
|
||
|
|
);
|
||
|
|
|
||
|
|
// 不在这里更新预支单已核销金额,而是在执行核销时更新
|
||
|
|
// if (oldAdvanceId) {
|
||
|
|
// const amountDiff = amount - oldAmount;
|
||
|
|
// if (amountDiff !== 0) {
|
||
|
|
// await db.query(
|
||
|
|
// 'UPDATE advances SET total_reimbursed = total_reimbursed + $1 WHERE id = $2',
|
||
|
|
// [amountDiff, oldAdvanceId]
|
||
|
|
// );
|
||
|
|
// }
|
||
|
|
// }
|
||
|
|
|
||
|
|
// 提交事务
|
||
|
|
await db.query('COMMIT');
|
||
|
|
|
||
|
|
if (result.changes > 0) {
|
||
|
|
res.json({ success: true, message: '更新成功' });
|
||
|
|
} else {
|
||
|
|
res.status(404).json({ success: false, message: '核销申请不存在' });
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
// 回滚事务
|
||
|
|
await db.query('ROLLBACK');
|
||
|
|
throw error;
|
||
|
|
}
|
||
|
|
} 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 {
|
||
|
|
// 获取核销金额和预支单ID
|
||
|
|
const verification = await db.query('SELECT amount, advance_id FROM verifications WHERE id = $1', [id]);
|
||
|
|
const amount = verification.rows[0]?.amount || 0;
|
||
|
|
const advanceId = verification.rows[0]?.advance_id;
|
||
|
|
|
||
|
|
// 删除核销申请
|
||
|
|
const result = await db.query('DELETE FROM verifications WHERE id = $1', [id]);
|
||
|
|
|
||
|
|
// 不在这里恢复预支单已核销金额,因为只有执行的核销才会增加已核销金额
|
||
|
|
// if (advanceId && amount > 0) {
|
||
|
|
// await db.query(
|
||
|
|
// 'UPDATE advances SET total_reimbursed = total_reimbursed - $1 WHERE id = $2',
|
||
|
|
// [amount, advanceId]
|
||
|
|
// );
|
||
|
|
// }
|
||
|
|
|
||
|
|
// 提交事务
|
||
|
|
await db.query('COMMIT');
|
||
|
|
|
||
|
|
if (result.changes > 0) {
|
||
|
|
res.json({ success: true, message: '删除成功' });
|
||
|
|
} else {
|
||
|
|
res.status(404).json({ success: false, message: '核销申请不存在' });
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
// 回滚事务
|
||
|
|
await db.query('ROLLBACK');
|
||
|
|
throw error;
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('删除核销申请失败:', error);
|
||
|
|
res.status(500).json({ success: false, message: '删除核销申请失败', error: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
router.post('/:id/submit', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { id } = req.params;
|
||
|
|
|
||
|
|
const result = await db.query('UPDATE verifications SET status = $1 WHERE id = $2', ['pending', id]);
|
||
|
|
|
||
|
|
if (result.changes > 0) {
|
||
|
|
res.json({ success: true, message: '提交成功' });
|
||
|
|
} else {
|
||
|
|
res.status(404).json({ success: false, message: '核销申请不存在' });
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('提交核销申请失败:', error);
|
||
|
|
res.status(500).json({ success: false, message: '提交核销申请失败', error: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
router.post('/:id/withdraw', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { id } = req.params;
|
||
|
|
|
||
|
|
const result = await db.query('UPDATE verifications SET status = $1 WHERE id = $2', ['withdrawn', id]);
|
||
|
|
|
||
|
|
if (result.changes > 0) {
|
||
|
|
res.json({ success: true, message: '撤回成功' });
|
||
|
|
} else {
|
||
|
|
res.status(404).json({ success: false, message: '核销申请不存在' });
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('撤回核销申请失败:', error);
|
||
|
|
res.status(500).json({ success: false, message: '撤回核销申请失败', error: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
router.post('/:id/approve', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { id } = req.params;
|
||
|
|
const { remark } = req.body;
|
||
|
|
|
||
|
|
const result = await db.query('UPDATE verifications SET status = $1, approval_remark = $2 WHERE id = $3', ['approved', remark, id]);
|
||
|
|
|
||
|
|
if (result.changes > 0) {
|
||
|
|
res.json({ success: true, message: '审批通过成功' });
|
||
|
|
} else {
|
||
|
|
res.status(404).json({ success: false, message: '核销申请不存在' });
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('审批核销申请失败:', error);
|
||
|
|
res.status(500).json({ success: false, message: '审批核销申请失败', error: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
router.post('/:id/reject', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { id } = req.params;
|
||
|
|
const { rejectReason } = req.body;
|
||
|
|
|
||
|
|
// 开始事务
|
||
|
|
await db.query('BEGIN TRANSACTION');
|
||
|
|
|
||
|
|
try {
|
||
|
|
// 获取核销金额和预支单ID
|
||
|
|
const verification = await db.query('SELECT amount, advance_id FROM verifications WHERE id = $1', [id]);
|
||
|
|
const amount = verification.rows[0]?.amount || 0;
|
||
|
|
const advanceId = verification.rows[0]?.advance_id;
|
||
|
|
|
||
|
|
// 退回核销申请
|
||
|
|
const result = await db.query('UPDATE verifications SET status = $1 WHERE id = $2', ['pending_edit', id]);
|
||
|
|
|
||
|
|
// 不在这里恢复预支单已核销金额,因为只有执行的核销才会增加已核销金额
|
||
|
|
// if (advanceId && amount > 0) {
|
||
|
|
// await db.query(
|
||
|
|
// 'UPDATE advances SET total_reimbursed = total_reimbursed - $1 WHERE id = $2',
|
||
|
|
// [amount, advanceId]
|
||
|
|
// );
|
||
|
|
// }
|
||
|
|
|
||
|
|
// 提交事务
|
||
|
|
await db.query('COMMIT');
|
||
|
|
|
||
|
|
if (result.changes > 0) {
|
||
|
|
res.json({ success: true, message: '退回成功' });
|
||
|
|
} else {
|
||
|
|
res.status(404).json({ success: false, message: '核销申请不存在' });
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
// 回滚事务
|
||
|
|
await db.query('ROLLBACK');
|
||
|
|
throw error;
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('退回核销申请失败:', error);
|
||
|
|
res.status(500).json({ success: false, message: '退回核销申请失败', error: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
|
||
|
|
module.exports = router;
|