From ca910ee48940cf39b79f4c492fa378ca8f626b2d Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Fri, 23 Jan 2026 17:43:22 +0200 Subject: [PATCH] fix(server): customer/vendor opening balance: --- .../modules/Customers/CustomerGLEntries.ts | 196 ++++++++-------- .../Customers/CustomerGLEntriesStorage.ts | 155 ++++++------- .../src/modules/Customers/Customers.module.ts | 12 +- .../CustomerGLEntriesSubscriber.ts | 146 +++++------- .../src/modules/Vendors/VendorGLEntries.ts | 209 +++++++++--------- .../modules/Vendors/VendorGLEntriesStorage.ts | 160 +++++++------- .../src/modules/Vendors/Vendors.module.ts | 14 +- .../src/modules/Vendors/models/Vendor.ts | 1 + .../subscribers/VendorGLEntriesSubscriber.ts | 154 ++++++------- 9 files changed, 495 insertions(+), 552 deletions(-) diff --git a/packages/server/src/modules/Customers/CustomerGLEntries.ts b/packages/server/src/modules/Customers/CustomerGLEntries.ts index dc900c5a8..e35b8e674 100644 --- a/packages/server/src/modules/Customers/CustomerGLEntries.ts +++ b/packages/server/src/modules/Customers/CustomerGLEntries.ts @@ -1,117 +1,103 @@ -// import { Service, Inject } from 'typedi'; -// import { AccountNormal, ICustomer, ILedgerEntry } from '@/interfaces'; -// import Ledger from '@/services/Accounting/Ledger'; +import { Injectable } from '@nestjs/common'; +import { AccountNormal } from '@/interfaces/Account'; +import { ILedgerEntry } from '@/modules/Ledger/types/Ledger.types'; +import { Ledger } from '@/modules/Ledger/Ledger'; +import { Customer } from './models/Customer'; -// @Service() -// export class CustomerGLEntries { -// /** -// * Retrieves the customer opening balance common entry attributes. -// * @param {ICustomer} customer -// */ -// private getCustomerOpeningGLCommonEntry = (customer: ICustomer) => { -// return { -// exchangeRate: customer.openingBalanceExchangeRate, -// currencyCode: customer.currencyCode, +@Injectable() +export class CustomerGLEntries { + /** + * Retrieves the customer opening balance common entry attributes. + */ + private getCustomerOpeningGLCommonEntry = (customer: Customer) => { + return { + exchangeRate: customer.openingBalanceExchangeRate, + currencyCode: customer.currencyCode, -// transactionType: 'CustomerOpeningBalance', -// transactionId: customer.id, + transactionType: 'CustomerOpeningBalance', + transactionId: customer.id, -// date: customer.openingBalanceAt, -// userId: customer.userId, -// contactId: customer.id, + date: customer.openingBalanceAt, + contactId: customer.id, -// credit: 0, -// debit: 0, + credit: 0, + debit: 0, -// branchId: customer.openingBalanceBranchId, -// }; -// }; + branchId: customer.openingBalanceBranchId, + }; + }; -// /** -// * Retrieves the customer opening GL credit entry. -// * @param {number} ARAccountId -// * @param {ICustomer} customer -// * @returns {ILedgerEntry} -// */ -// private getCustomerOpeningGLCreditEntry = ( -// ARAccountId: number, -// customer: ICustomer -// ): ILedgerEntry => { -// const commonEntry = this.getCustomerOpeningGLCommonEntry(customer); + /** + * Retrieves the customer opening GL credit entry. + */ + private getCustomerOpeningGLCreditEntry = ( + ARAccountId: number, + customer: Customer + ): ILedgerEntry => { + const commonEntry = this.getCustomerOpeningGLCommonEntry(customer); -// return { -// ...commonEntry, -// credit: 0, -// debit: customer.localOpeningBalance, -// accountId: ARAccountId, -// accountNormal: AccountNormal.DEBIT, -// index: 1, -// }; -// }; + return { + ...commonEntry, + credit: 0, + debit: customer.localOpeningBalance, + accountId: ARAccountId, + accountNormal: AccountNormal.DEBIT, + index: 1, + }; + }; -// /** -// * Retrieves the customer opening GL debit entry. -// * @param {number} incomeAccountId -// * @param {ICustomer} customer -// * @returns {ILedgerEntry} -// */ -// private getCustomerOpeningGLDebitEntry = ( -// incomeAccountId: number, -// customer: ICustomer -// ): ILedgerEntry => { -// const commonEntry = this.getCustomerOpeningGLCommonEntry(customer); + /** + * Retrieves the customer opening GL debit entry. + */ + private getCustomerOpeningGLDebitEntry = ( + incomeAccountId: number, + customer: Customer + ): ILedgerEntry => { + const commonEntry = this.getCustomerOpeningGLCommonEntry(customer); -// return { -// ...commonEntry, -// credit: customer.localOpeningBalance, -// debit: 0, -// accountId: incomeAccountId, -// accountNormal: AccountNormal.CREDIT, + return { + ...commonEntry, + credit: customer.localOpeningBalance, + debit: 0, + accountId: incomeAccountId, + accountNormal: AccountNormal.CREDIT, -// index: 2, -// }; -// }; + index: 2, + }; + }; -// /** -// * Retrieves the customer opening GL entries. -// * @param {number} ARAccountId -// * @param {number} incomeAccountId -// * @param {ICustomer} customer -// * @returns {ILedgerEntry[]} -// */ -// public getCustomerOpeningGLEntries = ( -// ARAccountId: number, -// incomeAccountId: number, -// customer: ICustomer -// ) => { -// const debitEntry = this.getCustomerOpeningGLDebitEntry( -// incomeAccountId, -// customer -// ); -// const creditEntry = this.getCustomerOpeningGLCreditEntry( -// ARAccountId, -// customer -// ); -// return [debitEntry, creditEntry]; -// }; + /** + * Retrieves the customer opening GL entries. + */ + public getCustomerOpeningGLEntries = ( + ARAccountId: number, + incomeAccountId: number, + customer: Customer + ) => { + const debitEntry = this.getCustomerOpeningGLDebitEntry( + incomeAccountId, + customer + ); + const creditEntry = this.getCustomerOpeningGLCreditEntry( + ARAccountId, + customer + ); + return [debitEntry, creditEntry]; + }; -// /** -// * Retrieves the customer opening balance ledger. -// * @param {number} ARAccountId -// * @param {number} incomeAccountId -// * @param {ICustomer} customer -// * @returns {ILedger} -// */ -// public getCustomerOpeningLedger = ( -// ARAccountId: number, -// incomeAccountId: number, -// customer: ICustomer -// ) => { -// const entries = this.getCustomerOpeningGLEntries( -// ARAccountId, -// incomeAccountId, -// customer -// ); -// return new Ledger(entries); -// }; -// } + /** + * Retrieves the customer opening balance ledger. + */ + public getCustomerOpeningLedger = ( + ARAccountId: number, + incomeAccountId: number, + customer: Customer + ) => { + const entries = this.getCustomerOpeningGLEntries( + ARAccountId, + incomeAccountId, + customer + ); + return new Ledger(entries); + }; +} diff --git a/packages/server/src/modules/Customers/CustomerGLEntriesStorage.ts b/packages/server/src/modules/Customers/CustomerGLEntriesStorage.ts index 3d5a7ca15..18605d6b4 100644 --- a/packages/server/src/modules/Customers/CustomerGLEntriesStorage.ts +++ b/packages/server/src/modules/Customers/CustomerGLEntriesStorage.ts @@ -1,90 +1,79 @@ -// import { Knex } from 'knex'; -// import LedgerStorageService from '@/services/Accounting/LedgerStorageService'; -// import HasTenancyService from '@/services/Tenancy/TenancyService'; -// import { Service, Inject } from 'typedi'; -// import { CustomerGLEntries } from './CustomerGLEntries'; +import { Knex } from 'knex'; +import { Inject, Injectable } from '@nestjs/common'; +import { LedgerStorageService } from '@/modules/Ledger/LedgerStorage.service'; +import { AccountRepository } from '@/modules/Accounts/repositories/Account.repository'; +import { CustomerGLEntries } from './CustomerGLEntries'; +import { Customer } from './models/Customer'; +import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; -// @Service() -// export class CustomerGLEntriesStorage { -// @Inject() -// private tenancy: HasTenancyService; +@Injectable() +export class CustomerGLEntriesStorage { + constructor( + private readonly ledgerStorage: LedgerStorageService, + private readonly accountRepository: AccountRepository, + private readonly customerGLEntries: CustomerGLEntries, -// @Inject() -// private ledegrRepository: LedgerStorageService; + @Inject(Customer.name) + private readonly customerModel: TenantModelProxy, + ) { } -// @Inject() -// private customerGLEntries: CustomerGLEntries; + /** + * Customer opening balance journals. + */ + public writeCustomerOpeningBalance = async ( + customerId: number, + trx?: Knex.Transaction, + ) => { + const customer = await this.customerModel() + .query(trx) + .findById(customerId); -// /** -// * Customer opening balance journals. -// * @param {number} tenantId -// * @param {number} customerId -// * @param {Knex.Transaction} trx -// */ -// public writeCustomerOpeningBalance = async ( -// tenantId: number, -// customerId: number, -// trx?: Knex.Transaction -// ) => { -// const { Customer } = this.tenancy.models(tenantId); -// const { accountRepository } = this.tenancy.repositories(tenantId); + // Finds the income account. + const incomeAccount = await this.accountRepository.findOne({ + slug: 'other-income', + }); + // Find or create the A/R account. + const ARAccount = + await this.accountRepository.findOrCreateAccountReceivable( + customer.currencyCode, + {}, + trx, + ); + // Retrieves the customer opening balance ledger. + const ledger = this.customerGLEntries.getCustomerOpeningLedger( + ARAccount.id, + incomeAccount.id, + customer, + ); + // Commits the ledger entries to the storage. + await this.ledgerStorage.commit(ledger, trx); + }; -// const customer = await Customer.query(trx).findById(customerId); + /** + * Reverts the customer opening balance GL entries. + */ + public revertCustomerOpeningBalance = async ( + customerId: number, + trx?: Knex.Transaction, + ) => { + await this.ledgerStorage.deleteByReference( + customerId, + 'CustomerOpeningBalance', + trx, + ); + }; -// // Finds the income account. -// const incomeAccount = await accountRepository.findOne({ -// slug: 'other-income', -// }); -// // Find or create the A/R account. -// const ARAccount = await accountRepository.findOrCreateAccountReceivable( -// customer.currencyCode, -// {}, -// trx -// ); -// // Retrieves the customer opening balance ledger. -// const ledger = this.customerGLEntries.getCustomerOpeningLedger( -// ARAccount.id, -// incomeAccount.id, -// customer -// ); -// // Commits the ledger entries to the storage. -// await this.ledegrRepository.commit(tenantId, ledger, trx); -// }; + /** + * Writes the customer opening balance GL entries. + */ + public rewriteCustomerOpeningBalance = async ( + customerId: number, + trx?: Knex.Transaction, + ) => { + // Reverts the customer opening balance entries. + await this.revertCustomerOpeningBalance(customerId, trx); -// /** -// * Reverts the customer opening balance GL entries. -// * @param {number} tenantId -// * @param {number} customerId -// * @param {Knex.Transaction} trx -// */ -// public revertCustomerOpeningBalance = async ( -// tenantId: number, -// customerId: number, -// trx?: Knex.Transaction -// ) => { -// await this.ledegrRepository.deleteByReference( -// tenantId, -// customerId, -// 'CustomerOpeningBalance', -// trx -// ); -// }; - -// /** -// * Writes the customer opening balance GL entries. -// * @param {number} tenantId -// * @param {number} customerId -// * @param {Knex.Transaction} trx -// */ -// public rewriteCustomerOpeningBalance = async ( -// tenantId: number, -// customerId: number, -// trx?: Knex.Transaction -// ) => { -// // Reverts the customer opening balance entries. -// await this.revertCustomerOpeningBalance(tenantId, customerId, trx); - -// // Write the customer opening balance entries. -// await this.writeCustomerOpeningBalance(tenantId, customerId, trx); -// }; -// } + // Write the customer opening balance entries. + await this.writeCustomerOpeningBalance(customerId, trx); + }; +} diff --git a/packages/server/src/modules/Customers/Customers.module.ts b/packages/server/src/modules/Customers/Customers.module.ts index 7beb5a53b..6d192f9ae 100644 --- a/packages/server/src/modules/Customers/Customers.module.ts +++ b/packages/server/src/modules/Customers/Customers.module.ts @@ -18,9 +18,14 @@ import { GetCustomers } from './queries/GetCustomers.service'; import { DynamicListModule } from '../DynamicListing/DynamicList.module'; import { BulkDeleteCustomersService } from './BulkDeleteCustomers.service'; import { ValidateBulkDeleteCustomersService } from './ValidateBulkDeleteCustomers.service'; +import { LedgerModule } from '../Ledger/Ledger.module'; +import { AccountsModule } from '../Accounts/Accounts.module'; +import { CustomerGLEntries } from './CustomerGLEntries'; +import { CustomerGLEntriesStorage } from './CustomerGLEntriesStorage'; +import { CustomerWriteGLOpeningBalanceSubscriber } from './subscribers/CustomerGLEntriesSubscriber'; @Module({ - imports: [TenancyDatabaseModule, DynamicListModule], + imports: [TenancyDatabaseModule, DynamicListModule, LedgerModule, AccountsModule], controllers: [CustomersController], providers: [ ActivateCustomer, @@ -41,6 +46,9 @@ import { ValidateBulkDeleteCustomersService } from './ValidateBulkDeleteCustomer GetCustomers, BulkDeleteCustomersService, ValidateBulkDeleteCustomersService, + CustomerGLEntries, + CustomerGLEntriesStorage, + CustomerWriteGLOpeningBalanceSubscriber, ], }) -export class CustomersModule {} +export class CustomersModule { } diff --git a/packages/server/src/modules/Customers/subscribers/CustomerGLEntriesSubscriber.ts b/packages/server/src/modules/Customers/subscribers/CustomerGLEntriesSubscriber.ts index 1e5911f80..8d9578bc4 100644 --- a/packages/server/src/modules/Customers/subscribers/CustomerGLEntriesSubscriber.ts +++ b/packages/server/src/modules/Customers/subscribers/CustomerGLEntriesSubscriber.ts @@ -1,91 +1,63 @@ -// import { Service, Inject } from 'typedi'; -// import { -// ICustomerEventCreatedPayload, -// ICustomerEventDeletedPayload, -// ICustomerOpeningBalanceEditedPayload, -// } from '@/interfaces'; -// import events from '@/subscribers/events'; -// import { CustomerGLEntriesStorage } from '../CustomerGLEntriesStorage'; +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; +import { + ICustomerEventCreatedPayload, + ICustomerEventDeletedPayload, + ICustomerOpeningBalanceEditedPayload, +} from '../types/Customers.types'; +import { events } from '@/common/events/events'; +import { CustomerGLEntriesStorage } from '../CustomerGLEntriesStorage'; -// @Service() -// export class CustomerWriteGLOpeningBalanceSubscriber { -// @Inject() -// private customerGLEntries: CustomerGLEntriesStorage; +@Injectable() +export class CustomerWriteGLOpeningBalanceSubscriber { + constructor(private readonly customerGLEntries: CustomerGLEntriesStorage) { } -// /** -// * Attaches events with handlers. -// */ -// public attach(bus) { -// bus.subscribe( -// events.customers.onCreated, -// this.handleWriteOpenBalanceEntries -// ); -// bus.subscribe( -// events.customers.onDeleted, -// this.handleRevertOpeningBalanceEntries -// ); -// bus.subscribe( -// events.customers.onOpeningBalanceChanged, -// this.handleRewriteOpeningEntriesOnChanged -// ); -// } + /** + * Handles the writing opening balance journal entries once the customer created. + */ + @OnEvent(events.customers.onCreated) + public async handleWriteOpenBalanceEntries({ + customer, + trx, + }: ICustomerEventCreatedPayload) { + // Writes the customer opening balance journal entries. + if (customer.openingBalance) { + await this.customerGLEntries.writeCustomerOpeningBalance( + customer.id, + trx, + ); + } + } -// /** -// * Handles the writing opening balance journal entries once the customer created. -// * @param {ICustomerEventCreatedPayload} payload - -// */ -// private handleWriteOpenBalanceEntries = async ({ -// tenantId, -// customer, -// trx, -// }: ICustomerEventCreatedPayload) => { -// // Writes the customer opening balance journal entries. -// if (customer.openingBalance) { -// await this.customerGLEntries.writeCustomerOpeningBalance( -// tenantId, -// customer.id, -// trx -// ); -// } -// }; + /** + * Handles the deleting opening balance journal entries once the customer deleted. + */ + @OnEvent(events.customers.onDeleted) + public async handleRevertOpeningBalanceEntries({ + customerId, + trx, + }: ICustomerEventDeletedPayload) { + await this.customerGLEntries.revertCustomerOpeningBalance(customerId, trx); + } -// /** -// * Handles the deleting opeing balance journal entrise once the customer deleted. -// * @param {ICustomerEventDeletedPayload} payload - -// */ -// private handleRevertOpeningBalanceEntries = async ({ -// tenantId, -// customerId, -// trx, -// }: ICustomerEventDeletedPayload) => { -// await this.customerGLEntries.revertCustomerOpeningBalance( -// tenantId, -// customerId, -// trx -// ); -// }; - -// /** -// * Handles the rewrite opening balance entries once opening balnace changed. -// * @param {ICustomerOpeningBalanceEditedPayload} payload - -// */ -// private handleRewriteOpeningEntriesOnChanged = async ({ -// tenantId, -// customer, -// trx, -// }: ICustomerOpeningBalanceEditedPayload) => { -// if (customer.openingBalance) { -// await this.customerGLEntries.rewriteCustomerOpeningBalance( -// tenantId, -// customer.id, -// trx -// ); -// } else { -// await this.customerGLEntries.revertCustomerOpeningBalance( -// tenantId, -// customer.id, -// trx -// ); -// } -// }; -// } + /** + * Handles the rewrite opening balance entries once opening balance changed. + */ + @OnEvent(events.customers.onOpeningBalanceChanged) + public async handleRewriteOpeningEntriesOnChanged({ + customer, + trx, + }: ICustomerOpeningBalanceEditedPayload) { + if (customer.openingBalance) { + await this.customerGLEntries.rewriteCustomerOpeningBalance( + customer.id, + trx, + ); + } else { + await this.customerGLEntries.revertCustomerOpeningBalance( + customer.id, + trx, + ); + } + } +} diff --git a/packages/server/src/modules/Vendors/VendorGLEntries.ts b/packages/server/src/modules/Vendors/VendorGLEntries.ts index 073fe3d96..1c380bf6b 100644 --- a/packages/server/src/modules/Vendors/VendorGLEntries.ts +++ b/packages/server/src/modules/Vendors/VendorGLEntries.ts @@ -1,115 +1,116 @@ -// import { Service } from 'typedi'; -// import { IVendor, AccountNormal, ILedgerEntry } from '@/interfaces'; -// import Ledger from '@/services/Accounting/Ledger'; +import { Injectable } from '@nestjs/common'; +import { AccountNormal } from '@/interfaces/Account'; +import { ILedgerEntry } from '@/modules/Ledger/types/Ledger.types'; +import { Ledger } from '@/modules/Ledger/Ledger'; +import { Vendor } from './models/Vendor'; -// @Service() -// export class VendorGLEntries { -// /** -// * Retrieves the opening balance GL common entry. -// * @param {IVendor} vendor - -// */ -// private getOpeningBalanceGLCommonEntry = (vendor: IVendor) => { -// return { -// exchangeRate: vendor.openingBalanceExchangeRate, -// currencyCode: vendor.currencyCode, +@Injectable() +export class VendorGLEntries { + /** + * Retrieves the opening balance GL common entry. + * @param {Vendor} vendor - + */ + private getOpeningBalanceGLCommonEntry = (vendor: Vendor) => { + return { + exchangeRate: vendor.openingBalanceExchangeRate, + currencyCode: vendor.currencyCode, -// transactionType: 'VendorOpeningBalance', -// transactionId: vendor.id, + transactionType: 'VendorOpeningBalance', + transactionId: vendor.id, -// date: vendor.openingBalanceAt, -// userId: vendor.userId, -// contactId: vendor.id, + date: vendor.openingBalanceAt, + contactId: vendor.id, -// credit: 0, -// debit: 0, + credit: 0, + debit: 0, -// branchId: vendor.openingBalanceBranchId, -// }; -// }; + branchId: vendor.openingBalanceBranchId, + }; + }; -// /** -// * Retrieves the opening balance GL debit entry. -// * @param {number} costAccountId - -// * @param {IVendor} vendor -// * @returns {ILedgerEntry} -// */ -// private getOpeningBalanceGLDebitEntry = ( -// costAccountId: number, -// vendor: IVendor -// ): ILedgerEntry => { -// const commonEntry = this.getOpeningBalanceGLCommonEntry(vendor); + /** + * Retrieves the opening balance GL debit entry. + * @param {number} costAccountId - + * @param {Vendor} vendor + * @returns {ILedgerEntry} + */ + private getOpeningBalanceGLDebitEntry = ( + costAccountId: number, + vendor: Vendor + ): ILedgerEntry => { + const commonEntry = this.getOpeningBalanceGLCommonEntry(vendor); -// return { -// ...commonEntry, -// accountId: costAccountId, -// accountNormal: AccountNormal.DEBIT, -// debit: vendor.localOpeningBalance, -// credit: 0, -// index: 2, -// }; -// }; + return { + ...commonEntry, + accountId: costAccountId, + accountNormal: AccountNormal.DEBIT, + debit: vendor.localOpeningBalance, + credit: 0, + index: 2, + }; + }; -// /** -// * Retrieves the opening balance GL credit entry. -// * @param {number} APAccountId -// * @param {IVendor} vendor -// * @returns {ILedgerEntry} -// */ -// private getOpeningBalanceGLCreditEntry = ( -// APAccountId: number, -// vendor: IVendor -// ): ILedgerEntry => { -// const commonEntry = this.getOpeningBalanceGLCommonEntry(vendor); + /** + * Retrieves the opening balance GL credit entry. + * @param {number} APAccountId + * @param {Vendor} vendor + * @returns {ILedgerEntry} + */ + private getOpeningBalanceGLCreditEntry = ( + APAccountId: number, + vendor: Vendor + ): ILedgerEntry => { + const commonEntry = this.getOpeningBalanceGLCommonEntry(vendor); -// return { -// ...commonEntry, -// accountId: APAccountId, -// accountNormal: AccountNormal.CREDIT, -// credit: vendor.localOpeningBalance, -// index: 1, -// }; -// }; + return { + ...commonEntry, + accountId: APAccountId, + accountNormal: AccountNormal.CREDIT, + credit: vendor.localOpeningBalance, + index: 1, + }; + }; -// /** -// * Retrieves the opening balance GL entries. -// * @param {number} APAccountId -// * @param {number} costAccountId - -// * @param {IVendor} vendor -// * @returns {ILedgerEntry[]} -// */ -// public getOpeningBalanceGLEntries = ( -// APAccountId: number, -// costAccountId: number, -// vendor: IVendor -// ): ILedgerEntry[] => { -// const debitEntry = this.getOpeningBalanceGLDebitEntry( -// costAccountId, -// vendor -// ); -// const creditEntry = this.getOpeningBalanceGLCreditEntry( -// APAccountId, -// vendor -// ); -// return [debitEntry, creditEntry]; -// }; + /** + * Retrieves the opening balance GL entries. + * @param {number} APAccountId + * @param {number} costAccountId - + * @param {Vendor} vendor + * @returns {ILedgerEntry[]} + */ + public getOpeningBalanceGLEntries = ( + APAccountId: number, + costAccountId: number, + vendor: Vendor + ): ILedgerEntry[] => { + const debitEntry = this.getOpeningBalanceGLDebitEntry( + costAccountId, + vendor + ); + const creditEntry = this.getOpeningBalanceGLCreditEntry( + APAccountId, + vendor + ); + return [debitEntry, creditEntry]; + }; -// /** -// * Retrieves the opening balance ledger. -// * @param {number} APAccountId -// * @param {number} costAccountId - -// * @param {IVendor} vendor -// * @returns {Ledger} -// */ -// public getOpeningBalanceLedger = ( -// APAccountId: number, -// costAccountId: number, -// vendor: IVendor -// ) => { -// const entries = this.getOpeningBalanceGLEntries( -// APAccountId, -// costAccountId, -// vendor -// ); -// return new Ledger(entries); -// }; -// } + /** + * Retrieves the opening balance ledger. + * @param {number} APAccountId + * @param {number} costAccountId - + * @param {Vendor} vendor + * @returns {Ledger} + */ + public getOpeningBalanceLedger = ( + APAccountId: number, + costAccountId: number, + vendor: Vendor + ) => { + const entries = this.getOpeningBalanceGLEntries( + APAccountId, + costAccountId, + vendor + ); + return new Ledger(entries); + }; +} diff --git a/packages/server/src/modules/Vendors/VendorGLEntriesStorage.ts b/packages/server/src/modules/Vendors/VendorGLEntriesStorage.ts index a41bebb28..fdb230ae4 100644 --- a/packages/server/src/modules/Vendors/VendorGLEntriesStorage.ts +++ b/packages/server/src/modules/Vendors/VendorGLEntriesStorage.ts @@ -1,88 +1,86 @@ -// import { Knex } from 'knex'; -// import { Service, Inject } from 'typedi'; -// import LedgerStorageService from '@/services/Accounting/LedgerStorageService'; -// import HasTenancyService from '@/services/Tenancy/TenancyService'; -// import { VendorGLEntries } from './VendorGLEntries'; +import { Knex } from 'knex'; +import { Inject, Injectable } from '@nestjs/common'; +import { LedgerStorageService } from '@/modules/Ledger/LedgerStorage.service'; +import { AccountRepository } from '@/modules/Accounts/repositories/Account.repository'; +import { VendorGLEntries } from './VendorGLEntries'; +import { Vendor } from './models/Vendor'; +import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; -// @Service() -// export class VendorGLEntriesStorage { -// @Inject() -// private tenancy: HasTenancyService; +@Injectable() +export class VendorGLEntriesStorage { + constructor( + private readonly ledgerStorage: LedgerStorageService, + private readonly accountRepository: AccountRepository, + private readonly vendorGLEntries: VendorGLEntries, -// @Inject() -// private ledegrRepository: LedgerStorageService; + @Inject(Vendor.name) + private readonly vendorModel: TenantModelProxy, + ) { } -// @Inject() -// private vendorGLEntries: VendorGLEntries; + /** + * Vendor opening balance journals. + * @param {number} vendorId + * @param {Knex.Transaction} trx + */ + public writeVendorOpeningBalance = async ( + vendorId: number, + trx?: Knex.Transaction, + ) => { + const vendor = await this.vendorModel() + .query(trx) + .findById(vendorId); -// /** -// * Vendor opening balance journals. -// * @param {number} tenantId -// * @param {number} vendorId -// * @param {Knex.Transaction} trx -// */ -// public writeVendorOpeningBalance = async ( -// tenantId: number, -// vendorId: number, -// trx?: Knex.Transaction -// ) => { -// const { Vendor } = this.tenancy.models(tenantId); -// const { accountRepository } = this.tenancy.repositories(tenantId); + // Finds the expense account. + const expenseAccount = await this.accountRepository.findOrCreateOtherExpensesAccount( + {}, + trx, + ); + // Find or create the A/P account. + const APAccount = + await this.accountRepository.findOrCreateAccountsPayable( + vendor.currencyCode, + {}, + trx, + ); + // Retrieves the vendor opening balance ledger. + const ledger = this.vendorGLEntries.getOpeningBalanceLedger( + APAccount.id, + expenseAccount.id, + vendor, + ); + // Commits the ledger entries to the storage. + await this.ledgerStorage.commit(ledger, trx); + }; -// const vendor = await Vendor.query(trx).findById(vendorId); + /** + * Reverts the vendor opening balance GL entries. + * @param {number} vendorId + * @param {Knex.Transaction} trx + */ + public revertVendorOpeningBalance = async ( + vendorId: number, + trx?: Knex.Transaction, + ) => { + await this.ledgerStorage.deleteByReference( + vendorId, + 'VendorOpeningBalance', + trx, + ); + }; -// // Finds the expense account. -// const expenseAccount = await accountRepository.findOne({ -// slug: 'other-expenses', -// }); -// // Find or create the A/P account. -// const APAccount = await accountRepository.findOrCreateAccountsPayable( -// vendor.currencyCode, -// {}, -// trx -// ); -// // Retrieves the vendor opening balance ledger. -// const ledger = this.vendorGLEntries.getOpeningBalanceLedger( -// APAccount.id, -// expenseAccount.id, -// vendor -// ); -// // Commits the ledger entries to the storage. -// await this.ledegrRepository.commit(tenantId, ledger, trx); -// }; + /** + * Writes the vendor opening balance GL entries. + * @param {number} vendorId + * @param {Knex.Transaction} trx + */ + public rewriteVendorOpeningBalance = async ( + vendorId: number, + trx?: Knex.Transaction, + ) => { + // Reverts the vendor opening balance entries first. + await this.revertVendorOpeningBalance(vendorId, trx); -// /** -// * Reverts the vendor opening balance GL entries. -// * @param {number} tenantId -// * @param {number} vendorId -// * @param {Knex.Transaction} trx -// */ -// public revertVendorOpeningBalance = async ( -// tenantId: number, -// vendorId: number, -// trx?: Knex.Transaction -// ) => { -// await this.ledegrRepository.deleteByReference( -// tenantId, -// vendorId, -// 'VendorOpeningBalance', -// trx -// ); -// }; - -// /** -// * Writes the vendor opening balance GL entries. -// * @param {number} tenantId -// * @param {number} vendorId -// * @param {Knex.Transaction} trx -// */ -// public rewriteVendorOpeningBalance = async ( -// tenantId: number, -// vendorId: number, -// trx?: Knex.Transaction -// ) => { -// await this.writeVendorOpeningBalance(tenantId, vendorId, trx); - -// await this.revertVendorOpeningBalance(tenantId, vendorId, trx); -// }; -// } + // Write the vendor opening balance entries. + await this.writeVendorOpeningBalance(vendorId, trx); + }; +} diff --git a/packages/server/src/modules/Vendors/Vendors.module.ts b/packages/server/src/modules/Vendors/Vendors.module.ts index ae8c095b4..0bdba020e 100644 --- a/packages/server/src/modules/Vendors/Vendors.module.ts +++ b/packages/server/src/modules/Vendors/Vendors.module.ts @@ -18,9 +18,14 @@ import { VendorsExportable } from './VendorsExportable'; import { VendorsImportable } from './VendorsImportable'; import { BulkDeleteVendorsService } from './BulkDeleteVendors.service'; import { ValidateBulkDeleteVendorsService } from './ValidateBulkDeleteVendors.service'; +import { LedgerModule } from '../Ledger/Ledger.module'; +import { AccountsModule } from '../Accounts/Accounts.module'; +import { VendorGLEntries } from './VendorGLEntries'; +import { VendorGLEntriesStorage } from './VendorGLEntriesStorage'; +import { VendorsWriteGLOpeningSubscriber } from './subscribers/VendorGLEntriesSubscriber'; @Module({ - imports: [TenancyDatabaseModule, DynamicListModule], + imports: [TenancyDatabaseModule, DynamicListModule, LedgerModule, AccountsModule], controllers: [VendorsController], providers: [ ActivateVendorService, @@ -38,7 +43,10 @@ import { ValidateBulkDeleteVendorsService } from './ValidateBulkDeleteVendors.se TransformerInjectable, TenancyContext, VendorsExportable, - VendorsImportable + VendorsImportable, + VendorGLEntries, + VendorGLEntriesStorage, + VendorsWriteGLOpeningSubscriber, ], }) -export class VendorsModule {} +export class VendorsModule { } diff --git a/packages/server/src/modules/Vendors/models/Vendor.ts b/packages/server/src/modules/Vendors/models/Vendor.ts index 50fc57207..8ccd81640 100644 --- a/packages/server/src/modules/Vendors/models/Vendor.ts +++ b/packages/server/src/modules/Vendors/models/Vendor.ts @@ -36,6 +36,7 @@ export class Vendor extends TenantBaseModel { openingBalance: number; openingBalanceExchangeRate: number; openingBalanceAt: Date | string; + openingBalanceBranchId?: number; salutation: string; firstName: string; diff --git a/packages/server/src/modules/Vendors/subscribers/VendorGLEntriesSubscriber.ts b/packages/server/src/modules/Vendors/subscribers/VendorGLEntriesSubscriber.ts index 69b66dc18..31a5f4e89 100644 --- a/packages/server/src/modules/Vendors/subscribers/VendorGLEntriesSubscriber.ts +++ b/packages/server/src/modules/Vendors/subscribers/VendorGLEntriesSubscriber.ts @@ -1,91 +1,71 @@ -// import { Inject, Service } from 'typedi'; -// import events from '@/subscribers/events'; -// import { VendorGLEntriesStorage } from '../VendorGLEntriesStorage'; -// import { -// IVendorEventCreatedPayload, -// IVendorEventDeletedPayload, -// IVendorOpeningBalanceEditedPayload, -// } from '@/interfaces'; +import { Injectable } from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; +import { events } from '@/common/events/events'; +import { VendorGLEntriesStorage } from '../VendorGLEntriesStorage'; +import { + IVendorEventCreatedPayload, + IVendorEventDeletedPayload, + IVendorOpeningBalanceEditedPayload, +} from '../types/Vendors.types'; -// @Service() -// export class VendorsWriteGLOpeningSubscriber { -// @Inject() -// private vendorGLEntriesStorage: VendorGLEntriesStorage; +@Injectable() +export class VendorsWriteGLOpeningSubscriber { + constructor( + private readonly vendorGLEntriesStorage: VendorGLEntriesStorage, + ) {} -// /** -// * Constructor method. -// */ -// public attach(bus) { -// bus.subscribe( -// events.vendors.onCreated, -// this.handleWriteOpeningBalanceEntries -// ); -// bus.subscribe( -// events.vendors.onDeleted, -// this.handleRevertOpeningBalanceEntries -// ); -// bus.subscribe( -// events.vendors.onOpeningBalanceChanged, -// this.handleRewriteOpeningEntriesOnChanged -// ); -// } + /** + * Writes the open balance journal entries once the vendor created. + * @param {IVendorEventCreatedPayload} payload - + */ + @OnEvent(events.vendors.onCreated) + public async handleWriteOpeningBalanceEntries({ + vendor, + trx, + }: IVendorEventCreatedPayload) { + // Writes the vendor opening balance journal entries. + if (vendor.openingBalance) { + await this.vendorGLEntriesStorage.writeVendorOpeningBalance( + vendor.id, + trx, + ); + } + } -// /** -// * Writes the open balance journal entries once the vendor created. -// * @param {IVendorEventCreatedPayload} payload - -// */ -// private handleWriteOpeningBalanceEntries = async ({ -// tenantId, -// vendor, -// trx, -// }: IVendorEventCreatedPayload) => { -// // Writes the vendor opening balance journal entries. -// if (vendor.openingBalance) { -// await this.vendorGLEntriesStorage.writeVendorOpeningBalance( -// tenantId, -// vendor.id, -// trx -// ); -// } -// }; + /** + * Revert the opening balance journal entries once the vendor deleted. + * @param {IVendorEventDeletedPayload} payload - + */ + @OnEvent(events.vendors.onDeleted) + public async handleRevertOpeningBalanceEntries({ + vendorId, + trx, + }: IVendorEventDeletedPayload) { + await this.vendorGLEntriesStorage.revertVendorOpeningBalance( + vendorId, + trx, + ); + } -// /** -// * Revert the opening balance journal entries once the vendor deleted. -// * @param {IVendorEventDeletedPayload} payload - -// */ -// private handleRevertOpeningBalanceEntries = async ({ -// tenantId, -// vendorId, -// trx, -// }: IVendorEventDeletedPayload) => { -// await this.vendorGLEntriesStorage.revertVendorOpeningBalance( -// tenantId, -// vendorId, -// trx -// ); -// }; - -// /** -// * Handles the rewrite opening balance entries once opening balnace changed. -// * @param {ICustomerOpeningBalanceEditedPayload} payload - -// */ -// private handleRewriteOpeningEntriesOnChanged = async ({ -// tenantId, -// vendor, -// trx, -// }: IVendorOpeningBalanceEditedPayload) => { -// if (vendor.openingBalance) { -// await this.vendorGLEntriesStorage.rewriteVendorOpeningBalance( -// tenantId, -// vendor.id, -// trx -// ); -// } else { -// await this.vendorGLEntriesStorage.revertVendorOpeningBalance( -// tenantId, -// vendor.id, -// trx -// ); -// } -// }; -// } + /** + * Handles the rewrite opening balance entries once opening balance changed. + * @param {IVendorOpeningBalanceEditedPayload} payload - + */ + @OnEvent(events.vendors.onOpeningBalanceChanged) + public async handleRewriteOpeningEntriesOnChanged({ + vendor, + trx, + }: IVendorOpeningBalanceEditedPayload) { + if (vendor.openingBalance) { + await this.vendorGLEntriesStorage.rewriteVendorOpeningBalance( + vendor.id, + trx, + ); + } else { + await this.vendorGLEntriesStorage.revertVendorOpeningBalance( + vendor.id, + trx, + ); + } + } +}