6.0 KiB
6.0 KiB
Chart of Accounts - Quick Fix Implementation Guide
Priority: 🔴 Critical fixes to make routes accessible and secure
Fix 1: Register Routes in Main App
File: src/integration/api-gateway/app.ts
Add after line 252:
import chartOfAccountsRoutes from '@/core/accounting/chart-of-accounts.routes';
// ... existing code ...
app.use('/api/accounting/chart-of-accounts', chartOfAccountsRoutes);
Location: Add around line 252, after nostroVostroRoutes.
Fix 2: Fix Route Conflict
File: src/core/accounting/chart-of-accounts.routes.ts
Problem: /initialize route conflicts with /:accountCode route.
Solution: Move /initialize route BEFORE parameterized routes:
const router = Router();
// ✅ Initialize route FIRST (before parameterized routes)
router.post('/initialize', async (req, res) => {
// ... existing code ...
});
// Then other routes
router.get('/', async (req, res) => {
// ... existing code ...
});
// Parameterized routes come last
router.get('/:accountCode', async (req, res) => {
// ... existing code ...
});
Fix 3: Add Basic Authentication
File: src/core/accounting/chart-of-accounts.routes.ts
Add at top:
import { zeroTrustAuthMiddleware } from '@/integration/api-gateway/middleware/auth.middleware';
Protect sensitive routes:
// Initialize - Admin only
router.post('/initialize',
zeroTrustAuthMiddleware,
async (req, res) => {
// Check if user has admin role
if (req.user?.role !== 'ADMIN') {
return res.status(403).json({ error: 'Admin access required' });
}
// ... existing code ...
}
);
// Create - Accountant/Admin
router.post('/',
zeroTrustAuthMiddleware,
async (req, res) => {
if (!['ACCOUNTANT', 'ADMIN'].includes(req.user?.role || '')) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
// ... existing code ...
}
);
// Update - Accountant/Admin
router.put('/:accountCode',
zeroTrustAuthMiddleware,
async (req, res) => {
if (!['ACCOUNTANT', 'ADMIN'].includes(req.user?.role || '')) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
// ... existing code ...
}
);
Fix 4: Add Basic Input Validation
File: src/core/accounting/chart-of-accounts.routes.ts
Add validation helper:
function validateAccountCode(code: string): boolean {
return /^\d{4,10}$/.test(code);
}
function validateCategory(category: string): boolean {
return ['ASSET', 'LIABILITY', 'EQUITY', 'REVENUE', 'EXPENSE', 'OTHER'].includes(category);
}
function validateNormalBalance(balance: string): boolean {
return ['DEBIT', 'CREDIT'].includes(balance);
}
Add to POST route:
router.post('/', async (req, res) => {
try {
const { accountCode, accountName, category, normalBalance } = req.body;
// Validate required fields
if (!accountCode || !accountName || !category || !normalBalance) {
return res.status(400).json({ error: 'Missing required fields' });
}
// Validate format
if (!validateAccountCode(accountCode)) {
return res.status(400).json({ error: 'Account code must be 4-10 digits' });
}
if (!validateCategory(category)) {
return res.status(400).json({ error: 'Invalid category' });
}
if (!validateNormalBalance(normalBalance)) {
return res.status(400).json({ error: 'Normal balance must be DEBIT or CREDIT' });
}
const account = await chartOfAccountsService.createAccount(req.body);
res.status(201).json({ account });
} catch (error: any) {
res.status(400).json({ error: error.message });
}
});
Fix 5: Add Parent Account Validation
File: src/core/accounting/chart-of-accounts.service.ts
Update createAccount method:
async createAccount(account: Omit<ChartOfAccount, 'id'>): Promise<ChartOfAccount> {
// Validate parent exists if provided
if (account.parentAccountCode) {
const parent = await this.getAccountByCode(account.parentAccountCode);
if (!parent) {
throw new Error(`Parent account ${account.parentAccountCode} not found`);
}
// Validate category matches parent
if (parent.category !== account.category) {
throw new Error(`Account category must match parent category (${parent.category})`);
}
// Validate level is parent level + 1
if (account.level !== parent.level + 1) {
throw new Error(`Account level must be ${parent.level + 1} (parent level + 1)`);
}
}
// Validate normal balance matches category
const expectedBalance = this.getExpectedNormalBalance(account.category);
if (account.normalBalance !== expectedBalance) {
throw new Error(`Normal balance for ${account.category} should be ${expectedBalance}`);
}
// ... rest of existing implementation
}
private getExpectedNormalBalance(category: AccountCategory): 'DEBIT' | 'CREDIT' {
switch (category) {
case AccountCategory.ASSET:
case AccountCategory.EXPENSE:
return 'DEBIT';
case AccountCategory.LIABILITY:
case AccountCategory.EQUITY:
case AccountCategory.REVENUE:
return 'CREDIT';
default:
return 'DEBIT';
}
}
Testing the Fixes
After implementing fixes 1-3, test:
# Test route registration
curl http://localhost:3000/api/accounting/chart-of-accounts
# Test initialize (should require auth)
curl -X POST http://localhost:3000/api/accounting/chart-of-accounts/initialize
# Test create with validation
curl -X POST http://localhost:3000/api/accounting/chart-of-accounts \
-H "Content-Type: application/json" \
-d '{"accountCode": "9999", "accountName": "Test Account"}'
# Should return validation error
Summary
These 5 fixes address the most critical issues:
- ✅ Routes will be accessible
- ✅ Route conflicts resolved
- ✅ Basic security added
- ✅ Input validation added
- ✅ Data integrity improved
Estimated Time: 2-3 hours
Priority: 🔴 Critical