431 lines
14 KiB
JavaScript
431 lines
14 KiB
JavaScript
const express = require('express');
|
|
const db = require('../db');
|
|
const { authenticate, requireAdmin } = require('../middleware/auth');
|
|
const LedgerService = require('../services/ledgerService');
|
|
|
|
const router = express.Router();
|
|
|
|
router.get('/', async (req, res) => {
|
|
try {
|
|
const result = await db.query(`
|
|
SELECT * FROM suppliers
|
|
ORDER BY created_at DESC
|
|
LIMIT 50
|
|
`);
|
|
|
|
// 为每个供应商获取联系人和收款信息
|
|
const suppliersWithDetails = await Promise.all(
|
|
result.rows.map(async (supplier) => {
|
|
// 获取联系人信息
|
|
const contactsResult = await db.query(
|
|
`SELECT * FROM contacts WHERE entity_id = $1 AND entity_type = 'supplier' ORDER BY is_primary DESC`,
|
|
[supplier.id]
|
|
);
|
|
|
|
const contacts = contactsResult.rows.map(contact => ({
|
|
name: contact.name || '未命名',
|
|
position: contact.position || '',
|
|
phone: contact.phone || '',
|
|
is_primary: contact.is_primary === 1
|
|
}));
|
|
|
|
// 获取收款信息
|
|
const paymentInfosResult = await db.query(
|
|
`SELECT * FROM supplier_payment_infos WHERE supplier_id = $1 ORDER BY is_default DESC`,
|
|
[supplier.id]
|
|
);
|
|
|
|
const paymentInfos = paymentInfosResult.rows.map(payment => ({
|
|
id: payment.id,
|
|
account_name: payment.account_name,
|
|
bank_account: payment.account_number,
|
|
bank_name: payment.bank_name,
|
|
qr_code: payment.qr_code,
|
|
is_primary: payment.is_default === 1
|
|
}));
|
|
|
|
return {
|
|
...supplier,
|
|
contacts: contacts.length > 0 ? contacts : [],
|
|
payment_infos: paymentInfos.length > 0 ? paymentInfos : []
|
|
};
|
|
})
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: suppliersWithDetails,
|
|
count: suppliersWithDetails.length
|
|
});
|
|
} 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 supplierResult = await db.query(`
|
|
SELECT * FROM suppliers
|
|
WHERE id = ?
|
|
`, [id]);
|
|
|
|
if (supplierResult.rows.length > 0) {
|
|
const supplier = supplierResult.rows[0];
|
|
|
|
// 获取供应商的所有联系人
|
|
const contactsResult = await db.query(`
|
|
SELECT * FROM contacts
|
|
WHERE entity_id = ? AND entity_type = 'supplier'
|
|
ORDER BY is_primary DESC
|
|
`, [id]);
|
|
|
|
// 转换联系人数据结构
|
|
const contacts = contactsResult.rows.map(contact => ({
|
|
name: contact.name || '未命名',
|
|
position: contact.position || '',
|
|
phone: contact.phone || '',
|
|
is_primary: contact.is_primary === 1
|
|
}));
|
|
|
|
// 获取供应商的所有收款信息
|
|
const paymentInfosResult = await db.query(`
|
|
SELECT * FROM supplier_payment_infos
|
|
WHERE supplier_id = ?
|
|
ORDER BY is_default DESC
|
|
`, [id]);
|
|
|
|
// 转换收款信息数据结构
|
|
const paymentInfos = paymentInfosResult.rows.map(payment => ({
|
|
id: payment.id,
|
|
account_name: payment.account_name,
|
|
bank_account: payment.account_number,
|
|
bank_name: payment.bank_name,
|
|
qr_code: payment.qr_code,
|
|
is_primary: payment.is_default === 1
|
|
}));
|
|
|
|
const ledger = await LedgerService.getSupplierLedger(id);
|
|
|
|
const formattedSupplier = {
|
|
id: supplier.id,
|
|
code: `S${String(supplier.id).padStart(4, '0')}`,
|
|
name: supplier.name || '未命名',
|
|
supply_category: supplier.supply_category || '电力设备',
|
|
country: supplier.country || 'Laos',
|
|
contacts: contacts.length > 0 ? contacts : [],
|
|
payment_infos: paymentInfos.length > 0 ? paymentInfos : [],
|
|
remark: supplier.remark || '',
|
|
total_purchase_amount: ledger.summary.total_order_amount,
|
|
total_paid: ledger.summary.total_paid_amount,
|
|
total_payable: ledger.summary.total_unpaid_amount,
|
|
ledger: ledger,
|
|
created_at: supplier.created_at
|
|
};
|
|
|
|
// 设置响应头确保UTF-8编码
|
|
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
res.json({
|
|
success: true,
|
|
data: formattedSupplier
|
|
});
|
|
} 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('/', async (req, res) => {
|
|
try {
|
|
const { name, supply_category, country, remark, contacts, payment_infos } = req.body;
|
|
|
|
// 从contacts中获取主联系人信息
|
|
const primaryContact = contacts?.find(c => c.is_primary) || contacts?.[0];
|
|
const contact = primaryContact?.name || '';
|
|
const position = primaryContact?.position || '';
|
|
const phone = primaryContact?.phone || '';
|
|
const email = ''; // 前端没有email字段
|
|
const address = ''; // 前端没有address字段
|
|
|
|
const result = await db.query(
|
|
`INSERT INTO suppliers (name, address, contact, position, phone, email, supply_category, country, remark, created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
|
|
[name, address, contact, position, phone, email, supply_category, country, remark]
|
|
);
|
|
|
|
const supplierId = (result.rows[0]?.id || result.rows?.[0]?.id);
|
|
|
|
// 插入联系人数据
|
|
if (contacts && contacts.length > 0) {
|
|
for (const contactItem of contacts) {
|
|
await db.query(
|
|
`INSERT INTO contacts (entity_id, entity_type, name, position, phone, is_primary, created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
|
|
[supplierId, 'supplier', contactItem.name, contactItem.position, contactItem.phone, contactItem.is_primary ? 1 : 0]
|
|
);
|
|
}
|
|
}
|
|
|
|
// 插入收款信息数据
|
|
if (payment_infos && payment_infos.length > 0) {
|
|
for (const paymentInfo of payment_infos) {
|
|
await db.query(
|
|
`INSERT INTO supplier_payment_infos (supplier_id, account_name, bank_account, bank_name, qr_code, is_primary, created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
|
|
[supplierId, paymentInfo.account_name, paymentInfo.bank_account, paymentInfo.bank_name, paymentInfo.qr_code, paymentInfo.is_primary ? 1 : 0]
|
|
);
|
|
}
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
message: '供应商创建成功',
|
|
data: {
|
|
id: supplierId,
|
|
code: `S${String(supplierId).padStart(4, '0')}`,
|
|
name,
|
|
supply_category,
|
|
country,
|
|
contacts: contacts || [],
|
|
payment_infos: payment_infos || [],
|
|
remark,
|
|
total_purchase_amount: 0,
|
|
total_paid: 0,
|
|
total_payable: 0,
|
|
created_at: new Date().toISOString()
|
|
}
|
|
});
|
|
} 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 { name, supply_category, country, remark, contacts, payment_infos } = req.body;
|
|
|
|
// 从contacts中获取主联系人信息
|
|
const primaryContact = contacts?.find(c => c.is_primary) || contacts?.[0];
|
|
const contact = primaryContact?.name || '';
|
|
const position = primaryContact?.position || '';
|
|
const phone = primaryContact?.phone || '';
|
|
const email = ''; // 前端没有email字段
|
|
const address = ''; // 前端没有address字段
|
|
|
|
await db.query(
|
|
`UPDATE suppliers
|
|
SET name = ?, address = ?, contact = ?, position = ?, phone = ?, email = ?, supply_category = ?, country = ?, remark = ?, updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = ?`,
|
|
[name, address, contact, position, phone, email, supply_category, country, remark, id]
|
|
);
|
|
|
|
// 删除旧的联系人数据
|
|
await db.query(`DELETE FROM contacts WHERE entity_id = $1 AND entity_type = 'supplier'`, [id]);
|
|
|
|
// 插入新的联系人数据
|
|
if (contacts && contacts.length > 0) {
|
|
for (const contactItem of contacts) {
|
|
await db.query(
|
|
`INSERT INTO contacts (entity_id, entity_type, name, position, phone, is_primary, created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
|
|
[id, 'supplier', contactItem.name, contactItem.position, contactItem.phone, contactItem.is_primary ? 1 : 0]
|
|
);
|
|
}
|
|
}
|
|
|
|
// 删除旧的收款信息数据
|
|
await db.query(`DELETE FROM supplier_payment_infos WHERE supplier_id = $1`, [id]);
|
|
|
|
// 插入新的收款信息数据
|
|
if (payment_infos && payment_infos.length > 0) {
|
|
for (const paymentInfo of payment_infos) {
|
|
await db.query(
|
|
`INSERT INTO supplier_payment_infos (supplier_id, account_name, bank_account, bank_name, qr_code, is_primary, created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
|
|
[id, paymentInfo.account_name, paymentInfo.bank_account, paymentInfo.bank_name, paymentInfo.qr_code, paymentInfo.is_primary ? 1 : 0]
|
|
);
|
|
}
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
message: '供应商更新成功',
|
|
data: {
|
|
id,
|
|
code: `S${String(id).padStart(4, '0')}`,
|
|
name,
|
|
supply_category,
|
|
country,
|
|
contacts: contacts || [],
|
|
payment_infos: payment_infos || [],
|
|
remark,
|
|
total_purchase_amount: 0,
|
|
total_paid: 0,
|
|
total_payable: 0,
|
|
created_at: new Date().toISOString()
|
|
}
|
|
});
|
|
} 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(`DELETE FROM contacts WHERE entity_id = $1 AND entity_type = 'supplier'`, [id]);
|
|
|
|
// 再删除供应商数据
|
|
const result = await db.query(`DELETE FROM suppliers WHERE id = $1`, [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.get('/:id/orders', async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
const ordersResult = await db.query(`
|
|
SELECT
|
|
po.id,
|
|
po.code,
|
|
po.project_id,
|
|
po.total_amount,
|
|
po.paid_amount,
|
|
(po.total_amount - po.paid_amount) as unpaid_amount,
|
|
po.order_date,
|
|
po.status,
|
|
po.currency,
|
|
p.name as project_name
|
|
FROM purchase_orders po
|
|
LEFT JOIN projects p ON po.project_id = p.id
|
|
WHERE po.supplier_id = ?
|
|
ORDER BY po.order_date DESC
|
|
`, [id]);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: ordersResult.rows
|
|
});
|
|
} catch (error) {
|
|
console.error('获取供应商订单列表失败:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: '获取供应商订单列表失败',
|
|
error: error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
router.get('/:id/ledger', async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
const summaryResult = await db.query(`
|
|
SELECT
|
|
COUNT(*) as order_count,
|
|
COALESCE(SUM(total_amount), 0) as total_amount,
|
|
COALESCE(SUM(paid_amount), 0) as paid_amount,
|
|
COALESCE(SUM(total_amount - paid_amount), 0) as unpaid_amount
|
|
FROM purchase_orders
|
|
WHERE supplier_id = ?
|
|
`, [id]);
|
|
|
|
const ordersResult = await db.query(`
|
|
SELECT
|
|
po.id,
|
|
po.code,
|
|
po.project_id,
|
|
po.total_amount,
|
|
po.paid_amount,
|
|
(po.total_amount - po.paid_amount) as unpaid_amount,
|
|
po.order_date,
|
|
po.status,
|
|
po.currency,
|
|
p.name as project_name
|
|
FROM purchase_orders po
|
|
LEFT JOIN projects p ON po.project_id = p.id
|
|
WHERE po.supplier_id = ?
|
|
ORDER BY po.order_date DESC
|
|
`, [id]);
|
|
|
|
const summary = summaryResult.rows[0] || {
|
|
order_count: 0,
|
|
total_amount: 0,
|
|
paid_amount: 0,
|
|
unpaid_amount: 0
|
|
};
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
summary: {
|
|
order_count: summary.order_count || 0,
|
|
total_amount: summary.total_amount || 0,
|
|
paid_amount: summary.paid_amount || 0,
|
|
unpaid_amount: summary.unpaid_amount || 0
|
|
},
|
|
orders: ordersResult.rows
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('获取供应商台账失败:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: '获取供应商台账失败',
|
|
error: error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
|
|
module.exports = router; |