import * as plugins from './plugins.js'; import { getDb, getDbSync } from './skr.database.js'; import type { TAccountType, TSKRType, IAccountData } from './skr.types.js'; const { SmartDataDbDoc, svDb, unI, index, searchable } = plugins.smartdata; @plugins.smartdata.Collection(() => getDbSync()) export class Account extends SmartDataDbDoc { @unI() public id: string; @svDb() @index() public accountNumber: string; @svDb() @searchable() public accountName: string; @svDb() @index() public accountClass: number; @svDb() public accountGroup: number; @svDb() public accountSubgroup: number; @svDb() public accountType: TAccountType; @svDb() @index() public skrType: TSKRType; @svDb() @searchable() public description: string; @svDb() public vatRate: number; @svDb() public balance: number; @svDb() public debitTotal: number; @svDb() public creditTotal: number; @svDb() public isActive: boolean; @svDb() public isSystemAccount: boolean; @svDb() public createdAt: Date; @svDb() public updatedAt: Date; constructor(data?: Partial) { super(); if (data) { this.id = plugins.smartunique.shortId(); this.accountNumber = data.accountNumber || ''; this.accountName = data.accountName || ''; this.accountClass = data.accountClass || 0; this.accountType = data.accountType || 'asset'; this.skrType = data.skrType || 'SKR03'; this.description = data.description || ''; this.vatRate = data.vatRate || 0; this.isActive = data.isActive !== undefined ? data.isActive : true; // Parse account structure from number if (this.accountNumber && this.accountNumber.length === 4) { this.accountClass = parseInt(this.accountNumber[0]); this.accountGroup = parseInt(this.accountNumber[1]); this.accountSubgroup = parseInt(this.accountNumber[2]); } else { this.accountGroup = 0; this.accountSubgroup = 0; } this.balance = 0; this.debitTotal = 0; this.creditTotal = 0; this.isSystemAccount = true; this.createdAt = new Date(); this.updatedAt = new Date(); } } public static async createAccount(data: IAccountData): Promise { const account = new Account(data); await account.save(); return account; } public static async getAccountByNumber( accountNumber: string, skrType: TSKRType, ): Promise { const account = await Account.getInstance({ accountNumber, skrType, }); return account; } public static async getAccountsByClass( accountClass: number, skrType: TSKRType, ): Promise { const accounts = await Account.getInstances({ accountClass, skrType, isActive: true, }); return accounts; } public static async getAccountsByType( accountType: TAccountType, skrType: TSKRType, ): Promise { const accounts = await Account.getInstances({ accountType, skrType, isActive: true, }); return accounts; } public static async searchAccounts( searchTerm: string, skrType?: TSKRType, ): Promise { const query: any = {}; if (skrType) { query.skrType = skrType; } const accounts = await Account.getInstances(query); // Filter by search term const lowerSearchTerm = searchTerm.toLowerCase(); return accounts.filter( (account) => account.accountNumber.includes(searchTerm) || account.accountName.toLowerCase().includes(lowerSearchTerm) || account.description.toLowerCase().includes(lowerSearchTerm), ); } public async updateBalance( debitAmount: number = 0, creditAmount: number = 0, ): Promise { this.debitTotal += debitAmount; this.creditTotal += creditAmount; // Calculate balance based on account type switch (this.accountType) { case 'asset': case 'expense': // Normal debit accounts this.balance = this.debitTotal - this.creditTotal; break; case 'liability': case 'equity': case 'revenue': // Normal credit accounts this.balance = this.creditTotal - this.debitTotal; break; } this.updatedAt = new Date(); await this.save(); } public async deactivate(): Promise { this.isActive = false; this.updatedAt = new Date(); await this.save(); } public async activate(): Promise { this.isActive = true; this.updatedAt = new Date(); await this.save(); } public getNormalBalance(): 'debit' | 'credit' { switch (this.accountType) { case 'asset': case 'expense': return 'debit'; case 'liability': case 'equity': case 'revenue': return 'credit'; } } public async beforeSave(): Promise { // Validate account number format if (!this.accountNumber || this.accountNumber.length !== 4) { throw new Error( `Invalid account number format: ${this.accountNumber}. Must be 4 digits.`, ); } // Validate account number is numeric if (!/^\d{4}$/.test(this.accountNumber)) { throw new Error( `Account number must contain only digits: ${this.accountNumber}`, ); } // Validate account class matches first digit const firstDigit = parseInt(this.accountNumber[0]); if (this.accountClass !== firstDigit) { throw new Error( `Account class ${this.accountClass} does not match account number ${this.accountNumber}`, ); } // Validate SKR type if (this.skrType !== 'SKR03' && this.skrType !== 'SKR04') { throw new Error(`Invalid SKR type: ${this.skrType}`); } } }