320 lines
No EOL
7.8 KiB
JavaScript
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; |