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 82 83 84 85 86 87 88 89 90 91 | import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model, Types } from 'mongoose'; import { AllocationRule } from '../schemas/allocations.schema'; import { PostingEngineService } from './posting-engine.service'; import { JournalEntry, JournalLine } from '../schemas/journal-entry.schema'; @Injectable() export class AllocationService { constructor( @InjectModel(AllocationRule.name) private readonly ruleModel: Model<AllocationRule>, @InjectModel(JournalEntry.name) private readonly journalModel: Model<JournalEntry>, @InjectModel(JournalLine.name) private readonly lineModel: Model<JournalLine>, private readonly postingEngine: PostingEngineService, ) {} async createAllocationRule(tenantId: string, data: any): Promise<AllocationRule> { const doc = new this.ruleModel({ ...data, tenantId: new Types.ObjectId(tenantId), sourceAccountId: new Types.ObjectId(data.sourceAccountId), distributions: data.distributions.map((d: any) => ({ targetAccountId: new Types.ObjectId(d.targetAccountId), ratioDecimal: d.ratioDecimal, dimensions: d.dimensions })) }); return doc.save(); } async runAllocationRule(tenantId: string, ruleId: string, totalAmountMinor: number, userId: string): Promise<JournalEntry> { const rule = await this.ruleModel.findOne({ _id: ruleId, tenantId }).exec(); if (!rule) throw new NotFoundException('Allocation Rule not found'); const jv = new this.journalModel({ tenantId: new Types.ObjectId(tenantId), legalEntityId: new Types.ObjectId(), accountingBookId: new Types.ObjectId(), journalNumber: `AL-${Date.now()}`, postingDate: new Date(), status: 'draft', journalSource: 'Adjustment', currency: 'INR', exchangeRate: 1.0 }); await jv.save(); // Source Account Credit (reducing cost) await this.lineModel.create({ journalEntryId: jv._id, ledgerAccountId: rule.sourceAccountId, debitAmountMinor: 0, creditAmountMinor: totalAmountMinor, description: `Allocation distribution: source charge out` }); // Distributions Debits for (const dist of rule.distributions) { const charge = Math.round(totalAmountMinor * dist.ratioDecimal); await this.lineModel.create({ journalEntryId: jv._id, ledgerAccountId: dist.targetAccountId, debitAmountMinor: charge, creditAmountMinor: 0, description: `Allocation receipt: distributed overhead`, dimensions: dist.dimensions }); } // Adjust rounding difference on the first target account if any const lines = await this.lineModel.find({ journalEntryId: jv._id }).exec(); let totalDebit = 0; let targetLines = lines.filter(l => l.debitAmountMinor > 0); for (const l of targetLines) { totalDebit += l.debitAmountMinor; } const diff = totalAmountMinor - totalDebit; if (diff !== 0 && targetLines.length > 0) { const firstLine = targetLines[0]; firstLine.debitAmountMinor += diff; await firstLine.save(); } await this.postingEngine.postJournalEntry(tenantId, jv._id.toString(), userId); return jv; } } |