@debito/hippo-lib
Version:
Double-entry accounting library for CouchDB
999 lines (752 loc) • 28.8 kB
Markdown
# Hippo-lib - Universal Double Entry Accounting Library
A comprehensive JavaScript library for double-entry accounting system using CouchDB for data storage. **Works in both Node.js and web browsers.**
## Features
- ✅ **Universal compatibility** - Works in Node.js and browsers (with bundlers)
- ✅ **Double-entry accounting system** with automatic balance validation
- ✅ **Chart of Accounts (COA) management** with hierarchy validation and templates
- ✅ **Journal entries** with posting system, force deletion, and reversal capabilities
- ✅ **Trial balance reporting** with formatted output
- ✅ **Account templates** for dynamic vendor, customer, and employee account creation
- ✅ **Journal templates** for standardized transaction patterns
- ✅ **CouchDB integration** with universal HTTP client
- ✅ **Comprehensive ledger management** with centralized balance updates and audit trail
- ✅ **Account filtering by tags** for categorization and search
- ✅ **Date filtering** for ledger entries with CouchDB query optimization
- ✅ **Template key tagging** for audit trails and business intelligence
- ✅ **Date-ordered ledger listing** with CouchDB-level sorting
## Installation
```bash
npm install @debito/hippo-lib
```
## Quick Start
### Node.js Usage
```javascript
const hippoLib = require('@debito/hippo-lib');
// Initialize with database credentials
await hippoLib.init('username', 'password', 'database-name');
// Load Chart of Accounts template
await hippoLib.COA.loadTemplate();
// Create accounts and journal entries
const cash = await hippoLib.Account.findByKey('cash');
await cash.updateBalance(1000);
```
### Browser Usage
**1. Install and bundle with webpack/rollup/vite:**
```bash
npm install @debito/hippo-lib
```
**2. Use in your JavaScript:**
```javascript
// Import with ES modules (bundler required)
import hippoLib from '@debito/hippo-lib';
// Initialize with database credentials
await hippoLib.init('username', 'password', 'database-name');
// All functionality works exactly the same as Node.js
await hippoLib.COA.loadTemplate();
const cash = await hippoLib.Account.findByKey('cash');
await cash.updateBalance(1000);
```
## Configuration
### Simple Initialization
No configuration files needed! Just call the init method:
```javascript
// Node.js with environment variables
require('dotenv').config();
const hippoLib = require('@debito/hippo-lib');
await hippoLib.init(
process.env.COUCHDB_USERNAME,
process.env.COUCHDB_PASSWORD,
process.env.HIPPO_DATABASE_NAME
);
```
### Optional: .env file for Node.js
Create a `.env` file in your project root:
```env
COUCHDB_USERNAME=admin
COUCHDB_PASSWORD=password
HIPPO_DATABASE_NAME=accounting-db
```
# Complete API Reference
## Core Classes
### hippoLib (Main Entry Point)
```javascript
const hippoLib = require('@debito/hippo-lib');
// Initialize the library
await hippoLib.init(username, password, database);
// Get database information
const dbInfo = await hippoLib.getDatabaseInfo();
```
**Available exports:**
- `Account` - Account management class
- `LedgerEntry` - Individual ledger entry class
- `JournalEntry` - Journal entry class
- `COA` - Chart of Accounts utilities
- `TrialBalance` - Trial balance reporting
- `LedgerTemplate` - Account template utilities
- `JournalTemplate` - Journal entry template utilities
### Account Class
Manages individual accounts with balance tracking and validation.
**Properties:**
- `key` - Unique account identifier (string)
- `label` - Display name (string)
- `balance` - Current balance (number, positive for debits in debit accounts)
- `accountType` - Account type ('asset', 'liability', 'equity', 'revenue', 'expense')
- `hierarchy` - Hierarchical path (dot-separated string)
- `tags` - Array of categorization tags
**Static Methods:**
```javascript
// Create new account
const account = await Account.new(key, label, accountType, balance, hierarchy, tags);
// Get account by internal ID
const account = await Account.get(accountId);
// Find account by key (preferred method)
const account = await Account.findByKey(key);
// List all accounts
const accounts = await Account.list();
// List accounts by tags
const vendorAccounts = await Account.listByTag('vendor');
const taggedAccounts = await Account.listByTag(['cash', 'petty']);
```
**Instance Methods:**
```javascript
// Update balance directly (use sparingly - prefer journal entries)
await account.updateBalance(newBalance);
// Update balance from ledger activity (used internally)
await account.updateBalance('debit', 500, 'add');
await account.updateBalance('credit', 300, 'reverse');
// Save changes to database
await account.save();
// Remove account (balance must be zero)
await account.remove();
// Check for unsaved changes
const hasChanges = account.isDirty();
// Get list of changed fields
const changes = account.getChanges();
// Revert unsaved changes
account.rollback();
// Check if account increases with debits
const isDebitAccount = account.isDebitAccount();
// Add ledger entry (used internally)
const ledgerEntry = await account.addLedgerEntry(amount, type, description, journalEntryId);
```
**Example Usage:**
```javascript
// Create a new cash account
const cash = await Account.new(
'petty-cash', // key
'Petty Cash Fund', // label
'asset', // accountType
500, // initial balance
'assets.cash-bank.petty-cash', // hierarchy
['cash', 'petty'] // tags
);
// Update the balance
await cash.updateBalance(750);
// Find an existing account
const existingAccount = await Account.findByKey('petty-cash');
```
### JournalEntry Class
Manages double-entry journal entries with automatic validation.
**Properties:**
- `id` - Journal entry ID (string)
- `lines` - Array of journal lines
- `description` - Entry description (string)
- `date` - Entry date (ISO string)
- `status` - Entry status ('posted', 'reversed', 'deleted')
- `tags` - Array of categorization tags
**Static Methods:**
```javascript
// Create new journal entry
const journal = await JournalEntry.new(description, tags);
// Get journal entry by ID
const journal = await JournalEntry.get(journalId);
// List all journal entries (sorted by date, latest first)
const journals = await JournalEntry.list();
// List with custom sorting
const oldestFirst = await JournalEntry.list({ descending: false });
```
**Instance Methods:**
```javascript
// Add line to journal entry
journal.addLine(accountKey, type, amount, description);
// Validate entry balances (debits = credits)
const isValid = journal.validate();
// Get total debit amount
const debitTotal = journal.getDebitTotal();
// Get total credit amount
const creditTotal = journal.getCreditTotal();
// Get balance (should be 0 for valid entries)
const balance = journal.getBalance();
// Post entry (creates ledger entries, updates account balances)
await journal.post();
// Save metadata changes (description, tags) without affecting ledger
await journal.save();
// Force delete entry and reverse all balances (ADMIN FUNCTION)
await journal.forceDelete(true);
// Reverse entry with offsetting journal entry
const reversingEntry = await journal.reverse('Correction needed');
```
**Example Usage:**
```javascript
// Create a sales transaction
const entry = await JournalEntry.new('Daily cash sales');
// Add debit line (cash received)
entry.addLine('cash', 'debit', 500, 'Cash received from sales');
// Add credit line (revenue recognized)
entry.addLine('sales-revenue', 'credit', 500, 'Product sales revenue');
// Validate and post
if (entry.validate()) {
await entry.post();
}
```
### LedgerEntry Class
Individual ledger entry records for audit trail.
**Properties:**
- `accountId` - Associated account ID (string)
- `amount` - Entry amount (number, always positive)
- `type` - 'debit' or 'credit'
- `description` - Entry description (string)
- `journalEntryId` - Associated journal entry ID (string)
- `date` - Entry date (ISO string)
**Static Methods:**
```javascript
// Create new ledger entry (usually done automatically)
const ledger = await LedgerEntry.new(accountId, amount, type, description, journalEntryId);
// Get ledger entry by ID
const ledger = await LedgerEntry.get(entryId);
// List entries by account
const entries = await LedgerEntry.listByAccount(accountId);
// List entries by journal entry
const entries = await LedgerEntry.listByJournalEntry(journalEntryId);
// List all ledger entries
const entries = await LedgerEntry.list();
// List with date filtering options
const recentEntries = await LedgerEntry.list({
startDate: '2025-07-01T00:00:00.000Z', // From July 1st
endDate: '2025-07-31T23:59:59.999Z', // To July 31st
descending: false // Oldest first
});
// Combine account and date filtering
const accountEntries = await LedgerEntry.listByAccount(accountId, {
startDate: new Date('2025-08-01').toISOString(),
descending: true // Latest first (default)
});
```
**Instance Methods:**
```javascript
// Save changes
await ledger.save();
// Check for changes
const hasChanges = ledger.isDirty();
// Get changes
const changes = ledger.getChanges();
// Revert changes
ledger.rollback();
```
## Chart of Accounts (COA) System
### COA Class
Manages hierarchical chart of accounts with templates.
**Static Methods:**
```javascript
// Load COA template and optionally create accounts
const coa = await COA.loadTemplate(templateName = 'default', createAccounts = true);
// Create accounts from template definition
const results = await COA.createAccountsFromTemplate(templateAccounts);
// Store COA template in database
const doc = await COA.storeCOATemplate(template, templateName);
// Load COA from database
const coa = await COA.loadCOAFromDB();
// Validate hierarchy path exists
const node = await COA.validateHierarchyPath(hierarchyPath);
// Check if path represents a group (non-posting) account
const isGroup = await COA.isGroupAccount(hierarchyPath);
// Get hierarchy paths at specific level
const paths = await COA.getHierarchyPaths(parentPath, level);
// Account management in COA
await COA.addAccountToCOA(accountData);
await COA.removeAccountFromCOA(accountKey);
await COA.updateAccountInCOA(accountKey, updates);
```
**Template Structure:**
The COA template includes:
- **Hierarchy:** Nested tree structure with codes and labels
- **Accounts:** Predefined accounts with types and hierarchies
- **Ledger Templates:** Account creation patterns
- **Journal Templates:** Transaction patterns
**Example Usage:**
```javascript
// Load default COA template
await COA.loadTemplate();
// Validate a hierarchy path
const node = await COA.validateHierarchyPath('assets.cash-bank.cash');
console.log(node.label); // "Cash"
// Check if it's a group account
const isGroup = await COA.isGroupAccount('assets.cash-bank');
console.log(isGroup); // true (group accounts can't have transactions)
```
## Template System
### LedgerTemplate Class
Creates standardized accounts for vendors, customers, employees, etc.
**Static Methods:**
```javascript
// Create account from template
const account = await LedgerTemplate.createAccountFromTemplate(templateKey, name, overrides);
// Get template definition
const template = await LedgerTemplate.getTemplate(templateKey);
// List all available templates
const templates = await LedgerTemplate.listTemplates();
// Add custom template
const coa = await LedgerTemplate.addTemplate(templateData);
// Remove template
const coa = await LedgerTemplate.removeTemplate(templateKey);
// Generate key from name
const key = LedgerTemplate.generateKey(name, pattern);
// Generate label from name
const label = LedgerTemplate.generateLabel(name, pattern);
// Utility methods
await LedgerTemplate.printTemplates();
await LedgerTemplate.demonstrateTemplate(templateKey, exampleName);
```
**Built-in Templates:**
- `supplier` - Supplier/vendor accounts (liability type)
- `customer` - Customer receivable accounts (asset type)
- `employee` - Employee payroll accounts (liability type)
- `fixed-asset` - Fixed asset accounts (asset type)
**Example Usage:**
```javascript
// Create a new supplier account
const supplier = await LedgerTemplate.createAccountFromTemplate(
'supplier',
'ABC Manufacturing Corp'
);
// Create with custom options
const customer = await LedgerTemplate.createAccountFromTemplate(
'customer',
'XYZ Retail Store',
{
balance: 500,
tags: ['premium', 'corporate']
}
);
// List available templates
const templates = await LedgerTemplate.listTemplates();
console.log(templates.map(t => `${t.key}: ${t.label}`));
```
### JournalTemplate Class
Creates standardized journal entries from predefined transaction patterns.
**Static Methods:**
```javascript
// Create journal entry from template
const journal = await JournalTemplate.createJournalEntryFromTemplate(
templateKey, // Template identifier
amount, // Transaction amount
options, // Variable substitutions (e.g., {vendor: 'abc-corp'})
description, // Entry description
date // Optional: ISO date string for historical entries
);
```
**Built-in Journal Templates:**
- `purchase-stock-cash` - Cash purchase from vendor
- `purchase-stock-bank` - Bank payment purchase from vendor
- `purchase-stock-credit` - Credit purchase (accounts payable)
- `vendor-payment-cash` - Cash payment to vendor
- `vendor-payment-bank` - Bank payment to vendor
- `purchase-general-cash` - Generic cash purchase without vendor
**Example Usage:**
```javascript
// Create a cash purchase entry
const purchase = await JournalTemplate.createJournalEntryFromTemplate(
'purchase-stock-cash',
1000,
{ vendor: 'abc-manufacturing' },
'Inventory purchase from ABC Manufacturing'
);
// Create a vendor payment
const payment = await JournalTemplate.createJournalEntryFromTemplate(
'vendor-payment-bank',
500,
{ vendor: 'abc-manufacturing' },
'Payment to ABC Manufacturing'
);
```
## Reporting
### TrialBalance Class
Generates trial balance reports with formatted output.
**Static Methods:**
```javascript
// Generate trial balance as of specific date (default: current date)
const trialBalance = await TrialBalance.asOfDate(date);
// Print formatted trial balance to console
TrialBalance.printToConsole(trialBalance, options);
// Get summary statistics
const summary = TrialBalance.getSummary(trialBalance);
```
**Trial Balance Data Structure:**
```javascript
{
metadata: {
asOfDate: "2024-01-01T00:00:00.000Z",
generatedAt: "2024-01-01T12:00:00.000Z",
totalAccounts: 25,
isBalanced: true
},
accounts: {
asset: [
{key: 'cash', label: 'Cash', balance: 1000, debit: 1000, credit: 0}
],
liability: [...],
equity: [...],
revenue: [...],
expense: [...]
},
totals: {
byGroup: {
asset: {debit: 5000, credit: 0, count: 10},
// ... other groups
},
overall: {debit: 15000, credit: 15000, difference: 0}
}
}
```
**Example Usage:**
```javascript
// Generate current trial balance
const tb = await TrialBalance.asOfDate();
// Print formatted report
TrialBalance.printToConsole(tb);
// Get summary data
const summary = TrialBalance.getSummary(tb);
console.log(`Total accounts: ${summary.totalAccounts}`);
console.log(`Is balanced: ${summary.isBalanced}`);
console.log(`Total debits: $${summary.totalDebits}`);
console.log(`Total credits: $${summary.totalCredits}`);
// Show accounts with zero balances
TrialBalance.printToConsole(tb, { showZeroBalances: true });
```
## Constants and Types
### Account Types
```javascript
const { ACCOUNT_TYPES } = require('@debito/hippo-lib/src/constants');
ACCOUNT_TYPES.ASSET // 'asset'
ACCOUNT_TYPES.LIABILITY // 'liability'
ACCOUNT_TYPES.EQUITY // 'equity'
ACCOUNT_TYPES.REVENUE // 'revenue'
ACCOUNT_TYPES.EXPENSE // 'expense'
```
### Transaction Types
```javascript
const { DEBIT, CREDIT } = require('@debito/hippo-lib/src/constants');
DEBIT // 'debit'
CREDIT // 'credit'
```
### Journal Status
```javascript
const { JOURNAL_STATUS } = require('@debito/hippo-lib/src/constants');
JOURNAL_STATUS.POSTED // 'posted'
JOURNAL_STATUS.REVERSED // 'reversed'
JOURNAL_STATUS.DELETED // 'deleted'
```
## Database Integration
### Document ID Patterns
- **Accounts:** `account-{key}` (e.g., `account-cash`)
- **Journal Entries:** `jentry_{timestamp}_{random}` (e.g., `jentry_1640995200000_abc123`)
- **Ledger Entries:** `lentry_{timestamp}_{random}` (e.g., `lentry_1640995200000_def456`)
- **COA Settings:** `settings-coa`
### Automatic Indexes
The library creates these CouchDB indexes automatically:
- `ledger-by-date` - Ledger entries sorted by date
- `ledger-by-account` - Ledger entries by account ID
- `ledger-by-journal` - Ledger entries by journal entry ID
- `account-by-name` - Accounts by name
## Journal Entry Lifecycle Management
### Overview
The journal entry system provides comprehensive lifecycle management including posting, reversal, and force deletion capabilities while maintaining proper accounting principles and audit trails.
### Journal Entry Status Flow
```
NEW → POSTED → REVERSED (via reversal entry)
↘ DELETED (via force delete - admin only)
```
### Key Lifecycle Operations
**1. Creating and Posting Entries**
```javascript
// Create new entry (automatically in POSTED status)
const entry = await JournalEntry.new('Sales transaction', ['sales']);
entry.addLine('cash', 'debit', 1000, 'Cash received');
entry.addLine('sales', 'credit', 1000, 'Sales revenue');
// Post the entry (creates ledger entries and updates balances)
await entry.post();
```
**2. Reversing Entries (Accounting Best Practice)**
```javascript
// Create offsetting reversal entry
const reversalEntry = await entry.reverse('Customer returned merchandise');
// Original entry status becomes 'reversed'
// New reversal entry is created with opposite amounts
// Both entries preserved for audit trail
```
**3. Force Deletion (Admin Function)**
```javascript
// CAUTION: Breaks audit trail - use only for corrections/cleanup
await entry.forceDelete(true); // Explicit confirmation required
// Removes all ledger entries
// Reverses all account balance changes
// Marks journal entry as 'deleted' (preserves document for audit)
```
**4. Centralized Balance Management**
```javascript
// All balance updates go through Account.updateBalance()
const account = await Account.findByKey('cash');
// Add activity (normal posting)
await account.updateBalance('debit', 500, 'add');
// Reverse activity (for deletions/corrections)
await account.updateBalance('debit', 500, 'reverse');
```
### Audit Trail and Data Integrity
- **Immutable Principle**: Posted entries cannot be modified, only reversed
- **Complete Audit Trail**: All transactions preserved with status tracking
- **Balance Consistency**: Centralized balance management prevents inconsistencies
- **Force Delete Safety**: Requires explicit confirmation and preserves audit trail
### Testing the Lifecycle
```bash
# Run comprehensive lifecycle test
node test/test-journal-entry-lifecycle.js
```
This test validates:
- ✅ Journal entry creation and posting
- ✅ Ledger entry creation and account balance updates
- ✅ Force deletion with balance reversal
- ✅ Complete cleanup and audit trail preservation
## Date Filtering System
### Overview
The date filtering system provides powerful query capabilities for ledger entries with CouchDB-level optimization for performance.
### Key Features
- **CouchDB Query Integration**: Date filters applied at database level using `$gte` and `$lte` operators
- **Backward Compatibility**: All existing code continues to work without changes
- **Flexible Options**: Combine date filtering with other query options
- **Universal Support**: Works with all LedgerEntry list methods
### Date Filtering Options
**Available Parameters:**
- `startDate` - ISO date string (inclusive) - filters entries on or after this date
- `endDate` - ISO date string (inclusive) - filters entries on or before this date
- `descending` - Boolean (default: true) - sort order for results
**Supported Methods:**
- `LedgerEntry.list(options)`
- `LedgerEntry.listByAccount(accountId, options)`
- `LedgerEntry.listByJournalEntry(journalId, options)`
### Date Filtering Examples
```javascript
// Filter entries from last month
const lastMonth = new Date();
lastMonth.setMonth(lastMonth.getMonth() - 1);
const monthlyEntries = await LedgerEntry.list({
startDate: lastMonth.toISOString()
});
// Filter entries for specific date range
const julyEntries = await LedgerEntry.list({
startDate: '2025-07-01T00:00:00.000Z',
endDate: '2025-07-31T23:59:59.999Z'
});
// Get account-specific entries for date range
const cashAccount = await Account.findByKey('cash');
const cashJulyEntries = await LedgerEntry.listByAccount(cashAccount._doc._id, {
startDate: '2025-07-01T00:00:00.000Z',
endDate: '2025-07-31T23:59:59.999Z',
descending: false // Oldest first
});
// Filter entries older than specific date
const oldEntries = await LedgerEntry.list({
endDate: '2025-06-30T23:59:59.999Z'
});
```
### Date Inheritance System
Ledger entries automatically inherit dates from journal entries:
```javascript
// Manual journal entry with custom date
const entry = await JournalEntry.new('Historical entry', ['historical']);
entry._doc.date = '2025-01-15T10:00:00.000Z'; // Set custom date
entry.addLine('cash', 'debit', 1000, 'Historical cash entry');
entry.addLine('sales', 'credit', 1000, 'Historical sales');
await entry.save(); // Ledger entries inherit this date
// Template entry with custom date
const templateEntry = await JournalTemplate.createJournalEntryFromTemplate(
'purchase-stock-cash',
500,
{ vendor: 'vendor-key' },
'Historical purchase',
'2025-01-20T00:00:00.000Z' // Custom date parameter
);
```
### Account Tag Filtering
Filter accounts by tags for better organization:
```javascript
// Find all vendor accounts
const vendorAccounts = await Account.listByTag('vendor');
// Find accounts with multiple possible tags
const cashAccounts = await Account.listByTag(['cash', 'petty', 'bank']);
// Case-insensitive partial matching
const supplierAccounts = await Account.listByTag('supplier');
```
## Complete Usage Examples
### Basic Accounting Workflow
```javascript
const hippoLib = require('@debito/hippo-lib');
// 1. Initialize
await hippoLib.init('admin', 'password', 'accounting-db');
// 2. Load Chart of Accounts
await hippoLib.COA.loadTemplate();
// 3. Create vendor account from template
const vendor = await hippoLib.LedgerTemplate.createAccountFromTemplate(
'supplier',
'Office Supply Corp'
);
// 4. Create purchase using journal template
const purchase = await hippoLib.JournalTemplate.createJournalEntryFromTemplate(
'purchase-stock-credit',
250,
{ vendor: vendor.key },
'Office supplies purchase'
);
// 5. Generate trial balance
const trialBalance = await hippoLib.TrialBalance.asOfDate();
hippoLib.TrialBalance.printToConsole(trialBalance);
```
### Manual Journal Entry Creation
```javascript
// Create a complex journal entry manually
const journal = await hippoLib.JournalEntry.new('Monthly payroll');
// Add multiple lines
journal.addLine('wages-expense', 'debit', 5000, 'Gross wages');
journal.addLine('payroll-taxes-expense', 'debit', 500, 'Employer payroll taxes');
journal.addLine('employee-taxes-payable', 'credit', 1000, 'Employee tax withholdings');
journal.addLine('wages-payable', 'credit', 4500, 'Net wages payable');
// Validate and save
if (journal.validate()) {
await journal.save();
console.log('Payroll journal entry posted successfully');
} else {
console.log('Entry does not balance!');
}
```
### Account Management
```javascript
// Create accounts with hierarchy and tags
const equipment = await hippoLib.Account.new(
'pos-system',
'Point of Sale System',
'asset',
2500,
'assets.fixed-assets.equipment.pos-system',
['equipment', 'technology', 'depreciable']
);
// Update account properties
equipment.tags.push('critical');
await equipment.save();
// Search and filter accounts
const allAccounts = await hippoLib.Account.list();
const assetAccounts = allAccounts.filter(acc => acc.accountType === 'asset');
const cashAccounts = allAccounts.filter(acc => acc.tags.includes('cash'));
```
## Browser Compatibility
### Supported Bundlers
- ✅ **Webpack** (all versions)
- ✅ **Rollup**
- ✅ **Parcel**
- ✅ **Browserify**
- ✅ **esbuild**
- ✅ **Vite**
### Browser Requirements
- Modern browsers with ES6+ support
- Fetch API support (or axios polyfill)
- Module bundler for CommonJS imports
### Example React Integration
```jsx
import React, { useEffect, useState } from 'react';
import hippoLib from '@debito/hippo-lib';
function AccountingDashboard() {
const [accounts, setAccounts] = useState([]);
const [trialBalance, setTrialBalance] = useState(null);
const [initialized, setInitialized] = useState(false);
useEffect(() => {
async function initializeAccounting() {
try {
// Initialize hippo-lib
await hippoLib.init(
process.env.REACT_APP_COUCH_USER,
process.env.REACT_APP_COUCH_PASS,
process.env.REACT_APP_COUCH_DB
);
// Load COA template
await hippoLib.COA.loadTemplate();
// Load accounts and trial balance
const accountList = await hippoLib.Account.list();
const tb = await hippoLib.TrialBalance.asOfDate();
setAccounts(accountList);
setTrialBalance(tb);
setInitialized(true);
} catch (error) {
console.error('Failed to initialize accounting:', error);
}
}
initializeAccounting();
}, []);
if (!initialized) return <div>Loading accounting system...</div>;
return (
<div>
<h1>Accounting Dashboard</h1>
<section>
<h2>Accounts ({accounts.length})</h2>
{accounts.map(account => (
<div key={account.key}>
<strong>{account.label}</strong>: ${account.balance}
<span> ({account.accountType})</span>
</div>
))}
</section>
<section>
<h2>Trial Balance Summary</h2>
{trialBalance && (
<div>
<p>Total Debits: ${trialBalance.totals.overall.debit}</p>
<p>Total Credits: ${trialBalance.totals.overall.credit}</p>
<p>Balanced: {trialBalance.metadata.isBalanced ? '✅' : '❌'}</p>
</div>
)}
</section>
</div>
);
}
export default AccountingDashboard;
```
## Data Storage
This library uses CouchDB for data persistence. Make sure you have a CouchDB instance running and accessible.
**CouchDB Setup:**
1. Install CouchDB locally or use a cloud service
2. Create a database for your application
3. Configure authentication credentials
4. Set CORS settings for browser access (if needed)
## Testing
Run the test suite:
```bash
# Run comprehensive test suite
npm test
# Individual tests
node test/test-clean.js # General accounting operations
node test/test-trial-balance.js # Trial balance functionality
node test/test-coa-load.js # COA template loading
node test/test-accounts-by-tag.js # Account tag filtering
node test/test-template-tagging.js # Template key tagging system
node test/test-ledger-date-ordering.js # Date-ordered ledger listing
node test/test-date-filtering-simple.js # Date filtering functionality
node test/test-journal-entry-lifecycle.js # Journal entry posting, deletion, and reversal
node test/test-journal-template.js # Journal templates
node test/test-ledger-template.js # Account templates
node test/test-couchdb-driver.js # CouchDB driver
```
## Complete Browser Guide
For detailed browser usage examples including React, Vue.js, and vanilla JavaScript setups, see **[WEB-USAGE.md](./WEB-USAGE.md)**.
## Contributing
Issues and pull requests are welcome on the project repository.
## License
ISC
---
**@debito/hippo-lib v1.10.0** - Universal double-entry accounting for Node.js and browsers 🎉