@debito/hippo-lib
Version:
Double-entry accounting library for CouchDB
105 lines (87 loc) • 3.95 kB
JavaScript
import JournalEntry from '../JournalEntry.js';
import COA from '../coa.js';
/**
* JournalTemplate class for creating journal entries from predefined templates
*/
class JournalTemplate {
/**
* List all available journal templates
* @returns {Promise<Array>} Array of journal template objects
*/
static async listTemplates() {
try {
// Get templates from COA settings
const coa = await COA.loadCOAFromDB();
return coa.journelTemplates || [];
} catch (error) {
throw new Error(`Failed to list journal templates: ${error.message}`);
}
}
/**
* Create a journal entry from a template
* @param {string} templateKey - The key of the template to use
* @param {number} amount - The transaction amount
* @param {Object} options - Options containing placeholder values (e.g., {vendor: "abc-corp"})
* @param {string} [description] - Optional description for the journal entry
* @param {string} [date] - Optional ISO date string for the journal entry
* @returns {Promise<JournalEntry>} The created and saved journal entry
*/
static async createJournalEntryFromTemplate(templateKey, amount, options = {}, description = null, date = null) {
if (!templateKey || !amount) {
throw new Error('Template key and amount are required');
}
// Get the COA document to access journal templates
const coaDoc = await COA.loadCOAFromDB();
if (!coaDoc.journelTemplates) {
throw new Error('No journal templates found in COA');
}
// Find the specified template
const template = coaDoc.journelTemplates.find(t => t.key === templateKey);
if (!template) {
throw new Error(`Journal template '${templateKey}' not found`);
}
// Create the journal entry with description and template tag
const entryDescription = description || template.description || `Entry from template: ${template.label}`;
const journalEntry = await JournalEntry.new(entryDescription, [templateKey]);
// Set the date if provided
if (date) {
journalEntry._doc.date = date;
}
// Process each line in the template
for (const line of template.lines) {
// Resolve account keys by replacing placeholders
const accountKey = this._resolveAccountKey(line['account-key'], options);
// Create line descriptions
const lineDescription = line.description || entryDescription;
// Add the lines to the journal entry
journalEntry.addLine(accountKey, line.type, amount, lineDescription);
}
// Post the journal entry (creates ledger entries and updates balances)
await journalEntry.post();
return journalEntry;
}
/**
* Resolve account key by replacing placeholders with actual values
* @param {string} accountKey - The account key that may contain placeholders
* @param {Object} options - Options containing placeholder values
* @returns {string} The resolved account key
* @private
*/
static _resolveAccountKey(accountKey, options) {
if (!accountKey) {
throw new Error('Account key is required');
}
// If it's a regular account key (no placeholder), return as-is
if (!accountKey.startsWith('$')) {
return accountKey;
}
// Extract the placeholder key (remove the $ prefix)
const placeholderKey = accountKey.substring(1);
// Look for the key in options
if (options[placeholderKey]) {
return options[placeholderKey];
}
throw new Error(`Placeholder '${placeholderKey}' not found in options. Available: ${Object.keys(options).join(', ')}`);
}
}
export default JournalTemplate;