npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@debito/hippo-lib

v1.13.0

Published

Double-entry accounting library for CouchDB

Readme

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

npm install @debito/hippo-lib

Quick Start

Node.js Usage

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:

npm install @debito/hippo-lib

2. Use in your 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:

// 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:

COUCHDB_USERNAME=admin
COUCHDB_PASSWORD=password
HIPPO_DATABASE_NAME=accounting-db

Complete API Reference

Core Classes

hippoLib (Main Entry Point)

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:

// 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:

// 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:

// 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:

// 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:

// 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:

// 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:

// 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:

// 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:

// 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:

// 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:

// 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:

// 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:

// 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:

// 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:

// 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:

{
    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:

// 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

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

const { DEBIT, CREDIT } = require('@debito/hippo-lib/src/constants');

DEBIT   // 'debit'
CREDIT  // 'credit'

Journal Status

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

// 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)

// 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)

// 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

// 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

# 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

// 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:

// 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:

// 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

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

// 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

// 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

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:

# 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.

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 🎉