采购详情支持手动修改

This commit is contained in:
2026-07-28 17:51:14 +08:00
parent 0883566045
commit 24bfe66773
7 changed files with 516 additions and 101 deletions

View File

@@ -1,7 +1,9 @@
// 采购相关路由
const express = require('express');
const { authMiddleware } = require('../middleware/auth');
const { authMiddleware, adminOnly } = require('../middleware/auth');
const { broadcastToRoom } = require('../ws');
const db = require('../db');
const { timestamp } = require('../utils');
const router = express.Router();
@@ -18,6 +20,90 @@ router.get('/purchases/:id', authMiddleware, (req, res) => {
res.json({ ...pur, history, attachments });
});
// 更新采购记录
router.put('/purchases/:id', authMiddleware, (req, res) => {
const old = db.prepare('SELECT * FROM purchases WHERE id = ?').get(req.params.id);
if (!old) return res.status(404).json({ error: '未找到' });
const now = timestamp();
const username = req.user.username;
const { item, quantity, unit_price, freight, payment_method, invoice_type, status, applicant, remarks, created_at } = req.body;
const newVals = {
item: item !== undefined ? String(item) : old.item,
quantity: quantity !== undefined ? Number(quantity) : old.quantity,
unit_price: unit_price !== undefined ? Number(unit_price) : old.unit_price,
freight: freight !== undefined ? Number(freight) : old.freight,
payment_method: payment_method !== undefined ? String(payment_method) : old.payment_method,
invoice_type: invoice_type !== undefined ? String(invoice_type) : old.invoice_type,
status: status !== undefined ? String(status) : old.status,
applicant: applicant !== undefined ? String(applicant) : old.applicant,
remarks: remarks !== undefined ? String(remarks) : old.remarks,
created_at: created_at !== undefined ? String(created_at) : old.created_at
};
newVals.amount = newVals.quantity * newVals.unit_price + newVals.freight;
const fieldLabels = {
item: '物品', quantity: '数量', unit_price: '单价', freight: '邮费', amount: '金额',
payment_method: '付款方式', invoice_type: '发票', status: '状态', applicant: '申请人', remarks: '备注', created_at: '时间'
};
const changes = [];
for (const [field, label] of Object.entries(fieldLabels)) {
if (String(newVals[field]) !== String(old[field])) {
changes.push(label + ': ' + (old[field] || '空') + ' -> ' + newVals[field]);
db.prepare('UPDATE purchases SET ' + field + ' = ? WHERE id = ?').run(newVals[field], old.id);
}
}
if (changes.length > 0) {
db.prepare('UPDATE purchases SET updated_at = ? WHERE id = ?').run(now, old.id);
db.prepare('INSERT INTO purchase_history (purchase_id, action, user, timestamp) VALUES (?,?,?,?)').run(old.id, '更新:' + changes.join(''), username, now);
broadcastToRoom(old.room_id, { type: 'purchase_updated' });
res.json({ success: true, changes });
} else {
res.json({ success: true, changes: [] });
}
});
// 删除采购记录(管理员)
router.delete('/purchases/:id', authMiddleware, adminOnly, (req, res) => {
const pur = db.prepare('SELECT * FROM purchases WHERE id = ?').get(req.params.id);
if (!pur) return res.status(404).json({ error: '未找到' });
db.prepare('DELETE FROM purchase_attachments WHERE purchase_id = ?').run(pur.id);
db.prepare('DELETE FROM purchase_history WHERE purchase_id = ?').run(pur.id);
db.prepare('DELETE FROM purchases WHERE id = ?').run(pur.id);
broadcastToRoom(pur.room_id, { type: 'purchase_updated' });
res.json({ success: true });
});
// 管理附件
router.post('/purchases/:id/attachments', authMiddleware, (req, res) => {
const pur = db.prepare('SELECT * FROM purchases WHERE id = ?').get(req.params.id);
if (!pur) return res.status(404).json({ error: '未找到' });
const now = timestamp();
const username = req.user.username;
const { file_paths } = req.body;
if (!file_paths || !file_paths.length) return res.status(400).json({ error: '缺少文件路径' });
const insertAttach = db.prepare('INSERT INTO purchase_attachments (purchase_id, file_path, uploaded_by, timestamp) VALUES (?,?,?,?)');
file_paths.forEach(fp => insertAttach.run(pur.id, fp, username, now));
db.prepare('INSERT INTO purchase_history (purchase_id, action, user, timestamp) VALUES (?,?,?,?)').run(pur.id, '添加附件:' + file_paths.length + ' 个', username, now);
broadcastToRoom(pur.room_id, { type: 'purchase_updated' });
res.json({ success: true });
});
router.delete('/purchases/:id/attachments', authMiddleware, (req, res) => {
const pur = db.prepare('SELECT * FROM purchases WHERE id = ?').get(req.params.id);
if (!pur) return res.status(404).json({ error: '未找到' });
const username = req.user.username;
const { file_path } = req.body;
if (!file_path) return res.status(400).json({ error: '缺少文件路径' });
db.prepare('DELETE FROM purchase_attachments WHERE purchase_id = ? AND file_path = ?').run(pur.id, file_path);
db.prepare('INSERT INTO purchase_history (purchase_id, action, user, timestamp) VALUES (?,?,?,?)').run(pur.id, '删除附件:' + file_path.split('/').pop(), username, timestamp());
broadcastToRoom(pur.room_id, { type: 'purchase_updated' });
res.json({ success: true });
});
router.get('/summary', authMiddleware, (req, res) => {
const username = req.user.username;
const isAdmin = req.user.isAdmin;
@@ -25,7 +111,7 @@ router.get('/summary', authMiddleware, (req, res) => {
if (isAdmin) {
rooms = db.prepare('SELECT id, name FROM rooms').all();
} else {
rooms = db.prepare("SELECT id, name FROM rooms WHERE white_list = '' OR (',' || white_list || ',' LIKE ?)").all(`%,${username},%`);
rooms = db.prepare("SELECT id, name FROM rooms WHERE white_list = '' OR (',' || white_list || ',' LIKE ?)").all('%,' + username + ',%');
}
const summary = rooms.map(room => {
const stats = db.prepare('SELECT COUNT(*) as count, SUM(amount) as total FROM purchases WHERE room_id = ?').get(room.id);
@@ -38,10 +124,10 @@ router.get('/rooms/:roomId/purchases/export', authMiddleware, (req, res) => {
const purchases = db.prepare('SELECT * FROM purchases WHERE room_id = ?').all(req.params.roomId);
let csv = '时间,事项,数量,单价,金额,邮费,付款方式,发票类型,状态,申请人,备注\n';
purchases.forEach(p => {
csv += `${p.created_at},"${p.item||''}",${p.quantity},${p.unit_price},${p.amount},${p.freight},"${p.payment_method||''}","${p.invoice_type||''}","${p.status}","${p.applicant}","${p.remarks||''}"\n`;
csv += p.created_at + ',"' + (p.item||'') + '",' + p.quantity + ',' + p.unit_price + ',' + p.amount + ',' + p.freight + ',"' + (p.payment_method||'') + '","' + (p.invoice_type||'') + '","' + p.status + '","' + p.applicant + '","' + (p.remarks||'') + '"\n';
});
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', `attachment; filename="purchases-${req.params.roomId}.csv"`);
res.setHeader('Content-Disposition', 'attachment; filename="purchases-' + req.params.roomId + '.csv"');
res.send('\uFEFF' + csv);
});