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

320 lines
No EOL
7.8 KiB
JavaScript

const express = require('express');
const router = express.Router();
const { chartOfAccountsRepository, generalLedgerRepository } = require('../repositories');
/**
* GET /api/plugins/financials/accounts
* Get all accounts for the site
*/
router.get('/', async (req, res) => {
try {
const { site_id } = req.query;
if (!site_id) {
return res.status(400).json({
success: false,
error: 'Missing required parameter: site_id'
});
}
// Get accounts from database using repository
const accounts = await chartOfAccountsRepository.findBySiteId(site_id, {
orderBy: 'account_code',
orderDirection: 'asc'
});
res.json({
success: true,
data: accounts,
message: 'Chart of accounts retrieved successfully'
});
} catch (error) {
console.error('Failed to get accounts:', error);
res.status(500).json({
success: false,
error: 'Failed to retrieve accounts',
message: error.message
});
}
});
/**
* GET /api/plugins/financials/accounts/:id
* Get account 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: 'Missing required parameter: site_id'
});
}
// Get account from database
const account = await chartOfAccountsRepository.findById(id);
if (!account) {
return res.status(404).json({
success: false,
error: 'Account not found'
});
}
// Verify account belongs to site
if (account.site_id !== site_id) {
return res.status(403).json({
success: false,
error: 'Access denied'
});
}
res.json({
success: true,
data: account,
message: 'Account details retrieved successfully'
});
} catch (error) {
console.error('Failed to get account:', error);
res.status(500).json({
success: false,
error: 'Failed to retrieve account',
message: error.message
});
}
});
/**
* POST /api/plugins/financials/accounts
* Create new account
*/
router.post('/', async (req, res) => {
try {
const { site_id, account_code, account_name, account_type, parent_account_id, description, is_active } = req.body;
// Validate required fields
if (!site_id || !account_code || !account_name || !account_type) {
return res.status(400).json({
success: false,
error: 'Missing required fields',
message: 'Site ID, account code, name, and type are required'
});
}
// Require authenticated user for created_by
if (!req.user || !req.user.id) {
return res.status(401).json({
success: false,
error: 'Authentication required'
});
}
// Create account using repository
const newAccount = await chartOfAccountsRepository.createAccount({
site_id,
account_code,
account_name,
account_type,
parent_account_id,
description,
is_active,
created_by: req.user.id
});
res.status(201).json({
success: true,
data: newAccount,
message: 'Account created successfully'
});
} catch (error) {
console.error('Failed to create account:', error);
// Friendly handling of unique-violation or duplicate
if (error && (error.code === '23505' || (typeof error.message === 'string' && /duplicate|exists/i.test(error.message)))) {
return res.status(409).json({
success: false,
error: 'Account code already exists',
details: error.detail || error.message
});
}
// Ensure JSON response even for unexpected errors
return res.status(500).json({
success: false,
error: 'Failed to create account',
details: error && (error.message || String(error))
});
}
});
/**
* PUT /api/plugins/financials/accounts/:id
* Update account
*/
router.put('/:id', async (req, res) => {
try {
const { id } = req.params;
const { site_id, account_name, description, is_active } = req.body;
if (!site_id) {
return res.status(400).json({
success: false,
error: 'Missing required parameter: site_id'
});
}
// Get existing account
const existingAccount = await chartOfAccountsRepository.findById(id);
if (!existingAccount) {
return res.status(404).json({
success: false,
error: 'Account not found'
});
}
// Verify account belongs to site
if (existingAccount.site_id !== site_id) {
return res.status(403).json({
success: false,
error: 'Access denied'
});
}
// Update account
const updatedAccount = await chartOfAccountsRepository.updateAccount(id, {
account_name,
description,
is_active
});
res.json({
success: true,
data: updatedAccount,
message: 'Account updated successfully'
});
} catch (error) {
console.error('Failed to update account:', error);
res.status(500).json({
success: false,
error: 'Failed to update account',
message: error.message
});
}
});
/**
* DELETE /api/plugins/financials/accounts/:id
* Delete account
*/
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: 'Missing required parameter: site_id'
});
}
// Get existing account
const existingAccount = await chartOfAccountsRepository.findById(id);
if (!existingAccount) {
return res.status(404).json({
success: false,
error: 'Account not found'
});
}
// Verify account belongs to site
if (existingAccount.site_id !== site_id) {
return res.status(403).json({
success: false,
error: 'Access denied'
});
}
// Check if account has transactions
const hasTransactions = await chartOfAccountsRepository.hasTransactions(id);
if (hasTransactions) {
return res.status(400).json({
success: false,
error: 'Cannot delete account with existing transactions',
message: 'Please deactivate the account instead'
});
}
// Delete account
await chartOfAccountsRepository.deleteById(id);
res.json({
success: true,
message: 'Account deleted successfully'
});
} catch (error) {
console.error('Failed to delete account:', error);
res.status(500).json({
success: false,
error: 'Failed to delete account',
message: error.message
});
}
});
/**
* GET /api/plugins/financials/accounts/:id/balance
* Get account balance
*/
router.get('/:id/balance', async (req, res) => {
try {
const { id } = req.params;
const { site_id, start_date, end_date } = req.query;
if (!site_id) {
return res.status(400).json({
success: false,
error: 'Missing required parameter: site_id'
});
}
// Get account
const account = await chartOfAccountsRepository.findById(id);
if (!account) {
return res.status(404).json({
success: false,
error: 'Account not found'
});
}
// Calculate balance from transaction lines using General Ledger
const options = {};
if (start_date) options.startDate = start_date;
if (end_date) options.endDate = end_date;
const balance = await generalLedgerRepository.getAccountBalance(id, site_id, options);
balance.currency = 'USD';
res.json({
success: true,
data: balance,
message: 'Account balance retrieved successfully'
});
} catch (error) {
console.error('Failed to get account balance:', error);
res.status(500).json({
success: false,
error: 'Failed to retrieve account balance',
message: error.message
});
}
});
module.exports = router;