plugin-financials/routes/transactions.js
2025-11-03 13:51:33 +02:00

269 lines
No EOL
7.1 KiB
JavaScript

const express = require('express');
const router = express.Router();
const { transactionRepository, transactionLineRepository } = require('../repositories');
/**
* GET /api/plugins/financials/transactions
* Get all transactions for a site
*/
router.get('/', async (req, res) => {
try {
const { site_id, type, status, start_date, end_date } = req.query;
if (!site_id) {
return res.status(400).json({
success: false,
error: 'site_id is required'
});
}
let transactions;
// Filter by type if provided
if (type) {
transactions = await transactionRepository.findByType(site_id, type);
}
// Filter by date range if provided
else if (start_date && end_date) {
transactions = await transactionRepository.findByDateRange(site_id, start_date, end_date);
}
// Filter by status if provided
else if (status) {
transactions = await transactionRepository.findByStatus(site_id, status);
}
// Get all transactions
else {
transactions = await transactionRepository.findBySiteId(site_id);
}
res.json({
success: true,
data: transactions,
message: 'Transactions retrieved successfully'
});
} catch (error) {
console.error('Failed to get transactions:', error);
res.status(500).json({
success: false,
error: 'Failed to retrieve transactions',
message: error.message
});
}
});
/**
* GET /api/plugins/financials/transactions/:id
* Get transaction by ID
*/
router.get('/:id', async (req, res) => {
try {
const { id } = req.params;
const { site_id } = req.query;
if (!site_id) {
return res.status(400).json({
success: false,
error: 'site_id is required'
});
}
const transaction = await transactionRepository.findById(id);
if (!transaction) {
return res.status(404).json({
success: false,
error: 'Transaction not found'
});
}
// Verify transaction belongs to site
if (transaction.site_id !== site_id) {
return res.status(403).json({
success: false,
error: 'Access denied'
});
}
// Get transaction lines for double-entry view
const lines = await transactionLineRepository.findByTransactionId(id);
res.json({
success: true,
data: {
...transaction,
lines
},
message: 'Transaction details retrieved successfully'
});
} catch (error) {
console.error('Failed to get transaction:', error);
res.status(500).json({
success: false,
error: 'Failed to retrieve transaction',
message: error.message
});
}
});
/**
* POST /api/plugins/financials/transactions
* Create a new transaction with double-entry validation
*/
router.post('/', async (req, res) => {
try {
const { site_id, transaction_date, reference_number, description, transaction_type, amount, currency, status, created_by, lines } = req.body;
// Validate required fields
if (!site_id || !transaction_date || !description || !transaction_type || amount === undefined) {
return res.status(400).json({
success: false,
error: 'Missing required fields: site_id, transaction_date, description, transaction_type, amount'
});
}
// If lines provided, validate double-entry balance
if (lines && Array.isArray(lines)) {
const totalDebits = lines.reduce((sum, line) => sum + parseFloat(line.debit_amount || 0), 0);
const totalCredits = lines.reduce((sum, line) => sum + parseFloat(line.credit_amount || 0), 0);
if (Math.abs(totalDebits - totalCredits) > 0.01) { // Allow small floating point differences
return res.status(400).json({
success: false,
error: 'Debits must equal credits',
message: `Total debits: ${totalDebits}, Total credits: ${totalCredits}`
});
}
}
// Create transaction
const transaction = await transactionRepository.createTransaction({
site_id,
transaction_date,
reference_number,
description,
transaction_type,
amount,
currency: currency || 'USD',
status: status || 'pending',
created_by: created_by || req.user?.id || 'system'
});
// Create transaction lines if provided
if (lines && Array.isArray(lines)) {
for (const line of lines) {
await transactionLineRepository.create({
transaction_id: transaction.id,
account_id: line.account_id,
debit_amount: line.debit_amount || 0,
credit_amount: line.credit_amount || 0,
description: line.description,
created_at: new Date().toISOString()
});
}
}
// Get the created lines to return with transaction
const createdLines = await transactionLineRepository.findByTransactionId(transaction.id);
res.status(201).json({
success: true,
data: {
...transaction,
lines: createdLines
},
message: 'Transaction created successfully'
});
} catch (error) {
console.error('Failed to create transaction:', error);
res.status(500).json({
success: false,
error: 'Failed to create transaction',
message: error.message
});
}
});
/**
* PUT /api/plugins/financials/transactions/:id/approve
* Approve a transaction
*/
router.put('/:id/approve', async (req, res) => {
try {
const { id } = req.params;
const { approved_by } = req.body;
if (!approved_by) {
return res.status(400).json({
success: false,
error: 'approved_by is required'
});
}
const transaction = await transactionRepository.approveTransaction(id, approved_by);
res.json({
success: true,
data: transaction,
message: 'Transaction approved successfully'
});
} catch (error) {
console.error('Failed to approve transaction:', error);
res.status(500).json({
success: false,
error: 'Failed to approve transaction',
message: error.message
});
}
});
/**
* DELETE /api/plugins/financials/transactions/:id
* Delete a transaction
*/
router.delete('/:id', async (req, res) => {
try {
const { id } = req.params;
const { site_id } = req.query;
if (!site_id) {
return res.status(400).json({
success: false,
error: 'site_id is required'
});
}
// Get transaction to verify it exists and belongs to site
const transaction = await transactionRepository.findById(id);
if (!transaction) {
return res.status(404).json({
success: false,
error: 'Transaction not found'
});
}
if (transaction.site_id !== site_id) {
return res.status(403).json({
success: false,
error: 'Access denied'
});
}
// Delete transaction (lines will be deleted via CASCADE)
await transactionRepository.deleteById(id);
res.json({
success: true,
message: 'Transaction deleted successfully'
});
} catch (error) {
console.error('Failed to delete transaction:', error);
res.status(500).json({
success: false,
error: 'Failed to delete transaction',
message: error.message
});
}
});
module.exports = router;