Files
defi-arbitrage/docs/accounting/CHART_OF_ACCOUNTS_QUICK_FIXES.md
T
2026-03-02 12:14:07 -08:00

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:

  1. ✅ Routes will be accessible
  2. ✅ Route conflicts resolved
  3. ✅ Basic security added
  4. ✅ Input validation added
  5. ✅ Data integrity improved

Estimated Time: 2-3 hours
Priority: 🔴 Critical