/** * 业务台账服务 * 为合作伙伴提供统一的业务台账数据 */ const db = require('../db'); class LedgerService { static async getSubcontractorLedger(subcontractorId) { try { const projectsResult = await db.query(` SELECT p.id, p.code as project_code, p.name, p.contract_amount, p.status FROM projects p WHERE p.subcontractor_id = ? ORDER BY p.created_at DESC `, [subcontractorId]); const projects = projectsResult.rows; const totalContract = projects.reduce((sum, p) => sum + (parseFloat(p.contract_amount) || 0), 0); let totalPaid = 0; try { const paymentsResult = await db.query(` SELECT COALESCE(SUM(amount), 0) as paid_amount FROM payment_requests WHERE payee_type = 'subcontractor' AND payee_id = ? AND status = 'paid' `, [subcontractorId]); totalPaid = parseFloat(paymentsResult.rows[0]?.paid_amount) || 0; } catch (e) { /* payment_requests may not have payee_type column */ } return { summary: { item_count: projects.length, total_contract_amount: totalContract, total_paid_amount: totalPaid, total_unpaid_amount: totalContract - totalPaid }, items: projects.map(p => ({ id: p.id, type: 'project', code: p.project_code, name: p.name, contract_amount: parseFloat(p.contract_amount) || 0, paid_amount: 0, unpaid_amount: parseFloat(p.contract_amount) || 0, status: p.status })) }; } catch (error) { console.error('获取分包商台账失败:', error); return { summary: { item_count: 0, total_contract_amount: 0, total_paid_amount: 0, total_unpaid_amount: 0 }, items: [] }; } } static async getCustomerLedger(customerId) { try { const projectsResult = await db.query(` SELECT p.id, p.code as project_code, p.name, p.contract_amount, p.status FROM projects p WHERE p.customer_id = ? ORDER BY p.created_at DESC `, [customerId]); const projects = projectsResult.rows; const totalContract = projects.reduce((sum, p) => sum + (parseFloat(p.contract_amount) || 0), 0); let totalReceived = 0; try { const receiptsResult = await db.query(` SELECT COALESCE(SUM(amount), 0) as received_amount FROM payment_records WHERE customer_id = ? AND type = 'income' `, [customerId]); totalReceived = parseFloat(receiptsResult.rows[0]?.received_amount) || 0; } catch (e) { /* ignore */ } return { summary: { item_count: projects.length, total_contract_amount: totalContract, total_received_amount: totalReceived, total_receivable_amount: totalContract - totalReceived }, items: projects.map(p => ({ id: p.id, type: 'project', code: p.project_code, name: p.name, contract_amount: parseFloat(p.contract_amount) || 0, received_amount: 0, receivable_amount: parseFloat(p.contract_amount) || 0, status: p.status })) }; } catch (error) { console.error('获取客户台账失败:', error); return { summary: { item_count: 0, total_contract_amount: 0, total_received_amount: 0, total_receivable_amount: 0 }, items: [] }; } } static async getSupplierLedger(supplierId) { try { const ordersResult = await db.query(` SELECT po.id, po.code, po.total_amount, po.status, po.created_at, 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.created_at DESC `, [supplierId]); const orders = ordersResult.rows; const totalAmount = orders.reduce((sum, o) => sum + (parseFloat(o.total_amount) || 0), 0); let totalPaid = 0; try { const paymentsResult = await db.query(` SELECT COALESCE(SUM(pp.actual_amount), 0) as paid_amount FROM payment_plans pp JOIN purchase_orders po ON pp.purchase_order_id = po.id WHERE po.supplier_id = ? AND pp.status = 'paid' `, [supplierId]); totalPaid = parseFloat(paymentsResult.rows[0]?.paid_amount) || 0; } catch (e) { /* ignore */ } return { summary: { item_count: orders.length, total_order_amount: totalAmount, total_paid_amount: totalPaid, total_unpaid_amount: totalAmount - totalPaid }, items: orders.map(o => ({ id: o.id, type: 'purchase_order', code: o.code, name: o.project_name || o.code, order_amount: parseFloat(o.total_amount) || 0, paid_amount: 0, unpaid_amount: parseFloat(o.total_amount) || 0, status: o.status, date: o.created_at })) }; } catch (error) { console.error('获取供应商台账失败:', error); return { summary: { item_count: 0, total_order_amount: 0, total_paid_amount: 0, total_unpaid_amount: 0 }, items: [] }; } } static async getLogisticsCompanyLedger(companyId) { try { const ordersResult = await db.query(` SELECT lr.id, lr.code, lr.ship_date, lr.status, lr.primary_freight, lr.primary_freight_currency, lr.primary_freight_status, lr.secondary_freight, lr.secondary_freight_currency, lr.secondary_freight_status, po.code as order_code, p.name as project_name FROM logistics_records lr LEFT JOIN purchase_orders po ON lr.purchase_order_id = po.id LEFT JOIN projects p ON po.project_id = p.id WHERE lr.logistics_company_id = ? ORDER BY lr.created_at DESC `, [companyId]); const orders = ordersResult.rows; const totalPrimary = orders.reduce((sum, o) => sum + (parseFloat(o.primary_freight) || 0), 0); const totalSecondary = orders.reduce((sum, o) => sum + (parseFloat(o.secondary_freight) || 0), 0); const paidPrimary = orders.filter(o => o.primary_freight_status === 'paid').reduce((sum, o) => sum + (parseFloat(o.primary_freight) || 0), 0); const paidSecondary = orders.filter(o => o.secondary_freight_status === 'paid').reduce((sum, o) => sum + (parseFloat(o.secondary_freight) || 0), 0); return { summary: { item_count: orders.length, total_primary_freight: totalPrimary, total_secondary_freight: totalSecondary, total_freight: totalPrimary + totalSecondary, paid_primary_freight: paidPrimary, paid_secondary_freight: paidSecondary, paid_amount: paidPrimary + paidSecondary, unpaid_amount: (totalPrimary + totalSecondary) - (paidPrimary + paidSecondary) }, items: orders.map(o => ({ id: o.id, type: 'logistics_record', code: o.code, name: o.order_code || o.code, project_name: o.project_name, primary_freight: parseFloat(o.primary_freight) || 0, primary_freight_currency: o.primary_freight_currency || 'CNY', primary_freight_status: o.primary_freight_status, secondary_freight: parseFloat(o.secondary_freight) || 0, secondary_freight_currency: o.secondary_freight_currency || 'LAK', secondary_freight_status: o.secondary_freight_status, status: o.status, date: o.ship_date })) }; } catch (error) { console.error('获取物流公司台账失败:', error); return { summary: { item_count: 0, total_primary_freight: 0, total_secondary_freight: 0, total_freight: 0, paid_primary_freight: 0, paid_secondary_freight: 0, paid_amount: 0, unpaid_amount: 0 }, items: [] }; } } } module.exports = LedgerService;