UNPKG

@debito/hippo-lib

Version:

Double-entry accounting library for CouchDB

999 lines (752 loc) 28.8 kB
# 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 🎉