Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model, Types } from 'mongoose'; import { LedgerAccount } from '../schemas/ledger-account.schema'; @Injectable() export class ChartOfAccountsService { constructor( @InjectModel(LedgerAccount.name) private readonly accountModel: Model<LedgerAccount>, ) {} async createAccount(tenantId: string, data: any): Promise<LedgerAccount> { const parentId = data.parentAccountId; // Prevent circular hierarchy if (parentId) { let currentParentId = new Types.ObjectId(parentId); const visited = new Set<string>(); while (currentParentId) { const pStr = currentParentId.toString(); if (visited.has(pStr)) { throw new BadRequestException('Circular hierarchy detected in Chart of Accounts parent relations'); } visited.add(pStr); const parentDoc = await this.accountModel.findOne({ _id: currentParentId, tenantId }).exec(); if (!parentDoc) { throw new NotFoundException(`Parent account ${pStr} not found`); } if (parentDoc.parentAccountId) { currentParentId = parentDoc.parentAccountId as any; } else { break; } } } const doc = new this.accountModel({ ...data, tenantId: new Types.ObjectId(tenantId), parentAccountId: parentId ? new Types.ObjectId(parentId) : undefined, legalEntityId: new Types.ObjectId(data.legalEntityId) }); return doc.save(); } async getAccountById(tenantId: string, id: string): Promise<LedgerAccount> { const acc = await this.accountModel.findOne({ _id: id, tenantId }).exec(); if (!acc) throw new NotFoundException('Ledger Account not found'); return acc; } async listAccounts(tenantId: string, legalEntityId?: string): Promise<LedgerAccount[]> { const filter: any = { tenantId: new Types.ObjectId(tenantId) }; if (legalEntityId) { filter.legalEntityId = new Types.ObjectId(legalEntityId); } return this.accountModel.find(filter).sort({ accountCode: 1 }).exec(); } async validateManualPosting(tenantId: string, accountId: string): Promise<void> { const account = await this.getAccountById(tenantId, accountId); if (account.isControlAccount) { throw new BadRequestException(`Manual journal posting is restricted for control account: ${account.accountCode} (${account.accountName})`); } } async setOpeningBalance(tenantId: string, accountId: string, amountMinor: number): Promise<LedgerAccount> { const account = await this.accountModel.findOneAndUpdate( { _id: accountId, tenantId }, { $set: { openingBalanceMinor: amountMinor, currentBalanceMinor: amountMinor } }, { new: true } ).exec(); if (!account) throw new NotFoundException('Ledger Account not found'); return account; } } |