From ca910ee48940cf39b79f4c492fa378ca8f626b2d Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Fri, 23 Jan 2026 17:43:22 +0200 Subject: [PATCH 1/2] 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, + ); + } + } +} From 04d065b969a56ac0b9a14de2196928eebadf438b Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sat, 24 Jan 2026 13:59:43 +0200 Subject: [PATCH 2/2] wip --- .../Validators/BillBranchSubscriber.ts | 4 +- .../CashflowBranchDTOValidatorSubscriber.ts | 2 +- .../ContactOpeningBalanceBranchSubscriber.ts | 10 ++--- .../CreditNoteBranchesSubscriber.ts | 4 +- .../CreditNoteRefundBranchSubscriber.ts | 2 +- .../Validators/ExpenseBranchSubscriber.ts | 4 +- ...toryAdjustmentBranchValidatorSubscriber.ts | 2 +- .../InvoiceBranchValidatorSubscriber.ts | 4 +- .../Validators/PaymentMadeBranchSubscriber.ts | 4 +- .../PaymentReceiveBranchSubscriber.ts | 4 +- .../SaleEstimateMultiBranchesSubscriber.ts | 4 +- .../SaleReceiptBranchesSubscriber.ts | 4 +- .../VendorCreditBranchSubscriber.ts | 4 +- .../VendorCreditRefundBranchSubscriber.ts | 2 +- .../Customers/CustomerGLEntriesStorage.ts | 11 +++-- .../modules/Customers/Customers.controller.ts | 7 +-- .../src/modules/Customers/Customers.module.ts | 9 +++- .../Customers/CustomersApplication.service.ts | 21 ++++----- .../commands/CreateCustomer.service.ts | 2 +- .../EditOpeningBalanceCustomer.service.ts | 6 +-- .../Customers/dtos/CreateCustomer.dto.ts | 6 ++- .../dtos/CustomerOpeningBalanceEdit.dto.ts | 44 +++++++++++++++++++ .../Customers/types/Customers.types.ts | 12 ++--- .../src/modules/Vendors/Vendors.controller.ts | 7 +-- .../Vendors/VendorsApplication.service.ts | 18 +++----- .../EditOpeningBalanceVendor.service.ts | 6 +-- .../modules/Vendors/dtos/CreateVendor.dto.ts | 10 ++++- .../dtos/VendorOpeningBalanceEdit.dto.ts | 44 +++++++++++++++++++ .../modules/Vendors/types/Vendors.types.ts | 12 ++--- 29 files changed, 174 insertions(+), 95 deletions(-) create mode 100644 packages/server/src/modules/Customers/dtos/CustomerOpeningBalanceEdit.dto.ts create mode 100644 packages/server/src/modules/Vendors/dtos/VendorOpeningBalanceEdit.dto.ts diff --git a/packages/server/src/modules/Branches/subscribers/Validators/BillBranchSubscriber.ts b/packages/server/src/modules/Branches/subscribers/Validators/BillBranchSubscriber.ts index 7c46b607e..cca717adc 100644 --- a/packages/server/src/modules/Branches/subscribers/Validators/BillBranchSubscriber.ts +++ b/packages/server/src/modules/Branches/subscribers/Validators/BillBranchSubscriber.ts @@ -17,7 +17,7 @@ export class BillBranchValidateSubscriber { * Validate branch existance on bill creating. * @param {IBillCreatingPayload} payload */ - @OnEvent(events.bill.onCreating) + @OnEvent(events.bill.onCreating, { suppressErrors: false }) async validateBranchExistanceOnBillCreating({ billDTO, }: IBillCreatingPayload) { @@ -30,7 +30,7 @@ export class BillBranchValidateSubscriber { * Validate branch existance once bill editing. * @param {IBillEditingPayload} payload */ - @OnEvent(events.bill.onEditing) + @OnEvent(events.bill.onEditing, { suppressErrors: false }) async validateBranchExistanceOnBillEditing({ billDTO }: IBillEditingPayload) { await this.validateBranchExistance.validateTransactionBranchWhenActive( billDTO.branchId, diff --git a/packages/server/src/modules/Branches/subscribers/Validators/CashflowBranchDTOValidatorSubscriber.ts b/packages/server/src/modules/Branches/subscribers/Validators/CashflowBranchDTOValidatorSubscriber.ts index ea6cf7d7e..adc1a19d7 100644 --- a/packages/server/src/modules/Branches/subscribers/Validators/CashflowBranchDTOValidatorSubscriber.ts +++ b/packages/server/src/modules/Branches/subscribers/Validators/CashflowBranchDTOValidatorSubscriber.ts @@ -14,7 +14,7 @@ export class CashflowBranchDTOValidatorSubscriber { * Validate branch existance once cashflow transaction creating. * @param {ICommandCashflowCreatingPayload} payload */ - @OnEvent(events.cashflow.onTransactionCreating) + @OnEvent(events.cashflow.onTransactionCreating, { suppressErrors: false }) async validateBranchExistanceOnCashflowTransactionCreating({ newTransactionDTO, }: ICommandCashflowCreatingPayload) { diff --git a/packages/server/src/modules/Branches/subscribers/Validators/ContactOpeningBalanceBranchSubscriber.ts b/packages/server/src/modules/Branches/subscribers/Validators/ContactOpeningBalanceBranchSubscriber.ts index a1e788e19..d10f8b6c6 100644 --- a/packages/server/src/modules/Branches/subscribers/Validators/ContactOpeningBalanceBranchSubscriber.ts +++ b/packages/server/src/modules/Branches/subscribers/Validators/ContactOpeningBalanceBranchSubscriber.ts @@ -15,13 +15,13 @@ import { export class ContactBranchValidateSubscriber { constructor( private readonly validateBranchExistance: ValidateBranchExistance, - ) { } + ) {} /** * Validate branch existance on customer creating. * @param {ICustomerEventCreatingPayload} payload */ - @OnEvent(events.customers.onCreating) + @OnEvent(events.customers.onCreating, { suppressErrors: false }) async validateBranchExistanceOnCustomerCreating({ customerDTO, }: ICustomerEventCreatingPayload) { @@ -37,7 +37,7 @@ export class ContactBranchValidateSubscriber { * Validate branch existance once customer opening balance editing. * @param {ICustomerOpeningBalanceEditingPayload} payload */ - @OnEvent(events.customers.onOpeningBalanceChanging) + @OnEvent(events.customers.onOpeningBalanceChanging, { suppressErrors: false }) async validateBranchExistanceOnCustomerOpeningBalanceEditing({ openingBalanceEditDTO, }: ICustomerOpeningBalanceEditingPayload) { @@ -52,7 +52,7 @@ export class ContactBranchValidateSubscriber { * Validates the branch existance on vendor creating. * @param {IVendorEventCreatingPayload} payload */ - @OnEvent(events.vendors.onCreating) + @OnEvent(events.vendors.onCreating, { suppressErrors: false }) async validateBranchExistanceonVendorCreating({ vendorDTO, }: IVendorEventCreatingPayload) { @@ -68,7 +68,7 @@ export class ContactBranchValidateSubscriber { * Validate branch existance once the vendor opening balance editing. * @param {IVendorOpeningBalanceEditingPayload} payload */ - @OnEvent(events.vendors.onOpeningBalanceChanging) + @OnEvent(events.vendors.onOpeningBalanceChanging, { suppressErrors: false }) async validateBranchExistanceOnVendorOpeningBalanceEditing({ openingBalanceEditDTO, }: IVendorOpeningBalanceEditingPayload) { diff --git a/packages/server/src/modules/Branches/subscribers/Validators/CreditNoteBranchesSubscriber.ts b/packages/server/src/modules/Branches/subscribers/Validators/CreditNoteBranchesSubscriber.ts index 9bfa75931..c75af2419 100644 --- a/packages/server/src/modules/Branches/subscribers/Validators/CreditNoteBranchesSubscriber.ts +++ b/packages/server/src/modules/Branches/subscribers/Validators/CreditNoteBranchesSubscriber.ts @@ -15,7 +15,7 @@ export class CreditNoteBranchValidateSubscriber { * Validate branch existance on credit note creating. * @param {ICreditNoteCreatingPayload} payload */ - @OnEvent(events.creditNote.onCreating) + @OnEvent(events.creditNote.onCreating, { suppressErrors: false }) async validateBranchExistanceOnCreditCreating({ creditNoteDTO, }: ICreditNoteCreatingPayload) { @@ -28,7 +28,7 @@ export class CreditNoteBranchValidateSubscriber { * Validate branch existance once credit note editing. * @param {ICreditNoteEditingPayload} payload */ - @OnEvent(events.creditNote.onEditing) + @OnEvent(events.creditNote.onEditing, { suppressErrors: false }) async validateBranchExistanceOnCreditEditing({ creditNoteEditDTO, }: ICreditNoteEditingPayload) { diff --git a/packages/server/src/modules/Branches/subscribers/Validators/CreditNoteRefundBranchSubscriber.ts b/packages/server/src/modules/Branches/subscribers/Validators/CreditNoteRefundBranchSubscriber.ts index ec6a03340..ac24f347e 100644 --- a/packages/server/src/modules/Branches/subscribers/Validators/CreditNoteRefundBranchSubscriber.ts +++ b/packages/server/src/modules/Branches/subscribers/Validators/CreditNoteRefundBranchSubscriber.ts @@ -14,7 +14,7 @@ export class CreditNoteRefundBranchValidateSubscriber { * Validate branch existance on refund credit note creating. * @param {IRefundCreditNoteCreatingPayload} payload */ - @OnEvent(events.creditNote.onRefundCreating) + @OnEvent(events.creditNote.onRefundCreating, { suppressErrors: false }) async validateBranchExistanceOnCreditRefundCreating({ newCreditNoteDTO, }: IRefundCreditNoteCreatingPayload) { diff --git a/packages/server/src/modules/Branches/subscribers/Validators/ExpenseBranchSubscriber.ts b/packages/server/src/modules/Branches/subscribers/Validators/ExpenseBranchSubscriber.ts index b67763e6b..d654c3302 100644 --- a/packages/server/src/modules/Branches/subscribers/Validators/ExpenseBranchSubscriber.ts +++ b/packages/server/src/modules/Branches/subscribers/Validators/ExpenseBranchSubscriber.ts @@ -16,7 +16,7 @@ export class ExpenseBranchValidateSubscriber { * Validate branch existance once expense transaction creating. * @param {IExpenseCreatingPayload} payload */ - @OnEvent(events.expenses.onCreating) + @OnEvent(events.expenses.onCreating, { suppressErrors: false }) async validateBranchExistanceOnExpenseCreating({ expenseDTO, }: IExpenseCreatingPayload) { @@ -29,7 +29,7 @@ export class ExpenseBranchValidateSubscriber { * Validate branch existance once expense transaction editing. * @param {IExpenseEventEditingPayload} payload */ - @OnEvent(events.expenses.onEditing) + @OnEvent(events.expenses.onEditing, { suppressErrors: false }) async validateBranchExistanceOnExpenseEditing({ expenseDTO, }: IExpenseEventEditingPayload) { diff --git a/packages/server/src/modules/Branches/subscribers/Validators/InventoryAdjustmentBranchValidatorSubscriber.ts b/packages/server/src/modules/Branches/subscribers/Validators/InventoryAdjustmentBranchValidatorSubscriber.ts index 8c0194f4d..6f2123cae 100644 --- a/packages/server/src/modules/Branches/subscribers/Validators/InventoryAdjustmentBranchValidatorSubscriber.ts +++ b/packages/server/src/modules/Branches/subscribers/Validators/InventoryAdjustmentBranchValidatorSubscriber.ts @@ -14,7 +14,7 @@ export class InventoryAdjustmentBranchValidateSubscriber { * Validate branch existance on inventory adjustment creating. * @param {IInventoryAdjustmentCreatingPayload} payload */ - @OnEvent(events.inventoryAdjustment.onQuickCreating) + @OnEvent(events.inventoryAdjustment.onQuickCreating, { suppressErrors: false }) async validateBranchExistanceOnInventoryCreating({ quickAdjustmentDTO, }: IInventoryAdjustmentCreatingPayload) { diff --git a/packages/server/src/modules/Branches/subscribers/Validators/InvoiceBranchValidatorSubscriber.ts b/packages/server/src/modules/Branches/subscribers/Validators/InvoiceBranchValidatorSubscriber.ts index 3f89b5a2e..8f8aae3e8 100644 --- a/packages/server/src/modules/Branches/subscribers/Validators/InvoiceBranchValidatorSubscriber.ts +++ b/packages/server/src/modules/Branches/subscribers/Validators/InvoiceBranchValidatorSubscriber.ts @@ -17,7 +17,7 @@ export class InvoiceBranchValidateSubscriber { * Validate branch existance on invoice creating. * @param {ISaleInvoiceCreatingPayload} payload */ - @OnEvent(events.saleInvoice.onCreating) + @OnEvent(events.saleInvoice.onCreating, { suppressErrors: false }) async validateBranchExistanceOnInvoiceCreating({ saleInvoiceDTO, }: ISaleInvoiceCreatingPaylaod) { @@ -30,7 +30,7 @@ export class InvoiceBranchValidateSubscriber { * Validate branch existance once invoice editing. * @param {ISaleInvoiceEditingPayload} payload */ - @OnEvent(events.saleInvoice.onEditing) + @OnEvent(events.saleInvoice.onEditing, { suppressErrors: false }) async validateBranchExistanceOnInvoiceEditing({ saleInvoiceDTO, }: ISaleInvoiceEditingPayload) { diff --git a/packages/server/src/modules/Branches/subscribers/Validators/PaymentMadeBranchSubscriber.ts b/packages/server/src/modules/Branches/subscribers/Validators/PaymentMadeBranchSubscriber.ts index feddaa0a9..63d79a7c2 100644 --- a/packages/server/src/modules/Branches/subscribers/Validators/PaymentMadeBranchSubscriber.ts +++ b/packages/server/src/modules/Branches/subscribers/Validators/PaymentMadeBranchSubscriber.ts @@ -17,7 +17,7 @@ export class PaymentMadeBranchValidateSubscriber { * Validate branch existance on estimate creating. * @param {ISaleEstimateCreatedPayload} payload */ - @OnEvent(events.billPayment.onCreating) + @OnEvent(events.billPayment.onCreating, { suppressErrors: false }) async validateBranchExistanceOnPaymentCreating({ billPaymentDTO, }: IBillPaymentCreatingPayload) { @@ -30,7 +30,7 @@ export class PaymentMadeBranchValidateSubscriber { * Validate branch existance once estimate editing. * @param {ISaleEstimateEditingPayload} payload */ - @OnEvent(events.billPayment.onEditing) + @OnEvent(events.billPayment.onEditing, { suppressErrors: false }) async validateBranchExistanceOnPaymentEditing({ billPaymentDTO, }: IBillPaymentEditingPayload) { diff --git a/packages/server/src/modules/Branches/subscribers/Validators/PaymentReceiveBranchSubscriber.ts b/packages/server/src/modules/Branches/subscribers/Validators/PaymentReceiveBranchSubscriber.ts index 133f84521..780590f4e 100644 --- a/packages/server/src/modules/Branches/subscribers/Validators/PaymentReceiveBranchSubscriber.ts +++ b/packages/server/src/modules/Branches/subscribers/Validators/PaymentReceiveBranchSubscriber.ts @@ -17,7 +17,7 @@ export class PaymentReceiveBranchValidateSubscriber { * Validate branch existance on estimate creating. * @param {IPaymentReceivedCreatingPayload} payload */ - @OnEvent(events.paymentReceive.onCreating) + @OnEvent(events.paymentReceive.onCreating, { suppressErrors: false }) async validateBranchExistanceOnPaymentCreating({ paymentReceiveDTO, }: IPaymentReceivedCreatingPayload) { @@ -30,7 +30,7 @@ export class PaymentReceiveBranchValidateSubscriber { * Validate branch existance once estimate editing. * @param {IPaymentReceivedEditingPayload} payload */ - @OnEvent(events.paymentReceive.onEditing) + @OnEvent(events.paymentReceive.onEditing, { suppressErrors: false }) async validateBranchExistanceOnPaymentEditing({ paymentReceiveDTO, }: IPaymentReceivedEditingPayload) { diff --git a/packages/server/src/modules/Branches/subscribers/Validators/SaleEstimateMultiBranchesSubscriber.ts b/packages/server/src/modules/Branches/subscribers/Validators/SaleEstimateMultiBranchesSubscriber.ts index 9774fd386..c4e1cdc41 100644 --- a/packages/server/src/modules/Branches/subscribers/Validators/SaleEstimateMultiBranchesSubscriber.ts +++ b/packages/server/src/modules/Branches/subscribers/Validators/SaleEstimateMultiBranchesSubscriber.ts @@ -17,7 +17,7 @@ export class SaleEstimateBranchValidateSubscriber { * Validate branch existance on estimate creating. * @param {ISaleEstimateCreatedPayload} payload */ - @OnEvent(events.saleEstimate.onCreating) + @OnEvent(events.saleEstimate.onCreating, { suppressErrors: false }) async validateBranchExistanceOnEstimateCreating({ estimateDTO, }: ISaleEstimateCreatingPayload) { @@ -30,7 +30,7 @@ export class SaleEstimateBranchValidateSubscriber { * Validate branch existance once estimate editing. * @param {ISaleEstimateEditingPayload} payload */ - @OnEvent(events.saleEstimate.onEditing) + @OnEvent(events.saleEstimate.onEditing, { suppressErrors: false }) async validateBranchExistanceOnEstimateEditing({ estimateDTO, }: ISaleEstimateEditingPayload) { diff --git a/packages/server/src/modules/Branches/subscribers/Validators/SaleReceiptBranchesSubscriber.ts b/packages/server/src/modules/Branches/subscribers/Validators/SaleReceiptBranchesSubscriber.ts index 751375558..d4e8fc513 100644 --- a/packages/server/src/modules/Branches/subscribers/Validators/SaleReceiptBranchesSubscriber.ts +++ b/packages/server/src/modules/Branches/subscribers/Validators/SaleReceiptBranchesSubscriber.ts @@ -17,7 +17,7 @@ export class SaleReceiptBranchValidateSubscriber { * Validate branch existance on estimate creating. * @param {ISaleReceiptCreatingPayload} payload */ - @OnEvent(events.saleReceipt.onCreating) + @OnEvent(events.saleReceipt.onCreating, { suppressErrors: false }) async validateBranchExistanceOnInvoiceCreating({ saleReceiptDTO, }: ISaleReceiptCreatingPayload) { @@ -30,7 +30,7 @@ export class SaleReceiptBranchValidateSubscriber { * Validate branch existance once estimate editing. * @param {ISaleReceiptEditingPayload} payload */ - @OnEvent(events.saleReceipt.onEditing) + @OnEvent(events.saleReceipt.onEditing, { suppressErrors: false }) async validateBranchExistanceOnInvoiceEditing({ saleReceiptDTO, }: ISaleReceiptEditingPayload) { diff --git a/packages/server/src/modules/Branches/subscribers/Validators/VendorCreditBranchSubscriber.ts b/packages/server/src/modules/Branches/subscribers/Validators/VendorCreditBranchSubscriber.ts index a545141fd..e129fbbd9 100644 --- a/packages/server/src/modules/Branches/subscribers/Validators/VendorCreditBranchSubscriber.ts +++ b/packages/server/src/modules/Branches/subscribers/Validators/VendorCreditBranchSubscriber.ts @@ -17,7 +17,7 @@ export class VendorCreditBranchValidateSubscriber { * Validate branch existance on estimate creating. * @param {ISaleEstimateCreatedPayload} payload */ - @OnEvent(events.vendorCredit.onCreating) + @OnEvent(events.vendorCredit.onCreating, { suppressErrors: false }) async validateBranchExistanceOnCreditCreating({ vendorCreditCreateDTO, }: IVendorCreditCreatingPayload) { @@ -30,7 +30,7 @@ export class VendorCreditBranchValidateSubscriber { * Validate branch existance once estimate editing. * @param {ISaleEstimateEditingPayload} payload */ - @OnEvent(events.vendorCredit.onEditing) + @OnEvent(events.vendorCredit.onEditing, { suppressErrors: false }) async validateBranchExistanceOnCreditEditing({ vendorCreditDTO, }: IVendorCreditEditingPayload) { diff --git a/packages/server/src/modules/Branches/subscribers/Validators/VendorCreditRefundBranchSubscriber.ts b/packages/server/src/modules/Branches/subscribers/Validators/VendorCreditRefundBranchSubscriber.ts index 4b4d43206..17bbf3d5b 100644 --- a/packages/server/src/modules/Branches/subscribers/Validators/VendorCreditRefundBranchSubscriber.ts +++ b/packages/server/src/modules/Branches/subscribers/Validators/VendorCreditRefundBranchSubscriber.ts @@ -14,7 +14,7 @@ export class VendorCreditRefundBranchValidateSubscriber { * Validate branch existance on refund credit note creating. * @param {IRefundVendorCreditCreatingPayload} payload */ - @OnEvent(events.vendorCredit.onRefundCreating) + @OnEvent(events.vendorCredit.onRefundCreating, { suppressErrors: false }) async validateBranchExistanceOnCreditRefundCreating({ refundVendorCreditDTO, }: IRefundVendorCreditCreatingPayload) { diff --git a/packages/server/src/modules/Customers/CustomerGLEntriesStorage.ts b/packages/server/src/modules/Customers/CustomerGLEntriesStorage.ts index 18605d6b4..fddd24556 100644 --- a/packages/server/src/modules/Customers/CustomerGLEntriesStorage.ts +++ b/packages/server/src/modules/Customers/CustomerGLEntriesStorage.ts @@ -5,6 +5,7 @@ import { AccountRepository } from '@/modules/Accounts/repositories/Account.repos import { CustomerGLEntries } from './CustomerGLEntries'; import { Customer } from './models/Customer'; import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel'; +import { Account } from '../Accounts/models/Account.model'; @Injectable() export class CustomerGLEntriesStorage { @@ -13,6 +14,9 @@ export class CustomerGLEntriesStorage { private readonly accountRepository: AccountRepository, private readonly customerGLEntries: CustomerGLEntries, + @Inject(Account.name) + private readonly accountModel: TenantModelProxy, + @Inject(Customer.name) private readonly customerModel: TenantModelProxy, ) { } @@ -29,9 +33,10 @@ export class CustomerGLEntriesStorage { .findById(customerId); // Finds the income account. - const incomeAccount = await this.accountRepository.findOne({ - slug: 'other-income', - }); + const incomeAccount = await this.accountModel() + .query(trx) + .findOne({ slug: 'other-income' }); + // Find or create the A/R account. const ARAccount = await this.accountRepository.findOrCreateAccountReceivable( diff --git a/packages/server/src/modules/Customers/Customers.controller.ts b/packages/server/src/modules/Customers/Customers.controller.ts index bded38902..910fda307 100644 --- a/packages/server/src/modules/Customers/Customers.controller.ts +++ b/packages/server/src/modules/Customers/Customers.controller.ts @@ -9,10 +9,7 @@ import { Query, } from '@nestjs/common'; import { CustomersApplication } from './CustomersApplication.service'; -import { - ICustomerOpeningBalanceEditDTO, - ICustomersFilter, -} from './types/Customers.types'; +import { CustomerOpeningBalanceEditDto } from './dtos/CustomerOpeningBalanceEdit.dto'; import { ApiOperation, ApiResponse, @@ -106,7 +103,7 @@ export class CustomersController { }) editOpeningBalance( @Param('id') customerId: number, - @Body() openingBalanceDTO: ICustomerOpeningBalanceEditDTO, + @Body() openingBalanceDTO: CustomerOpeningBalanceEditDto, ) { return this.customersApplication.editOpeningBalance( customerId, diff --git a/packages/server/src/modules/Customers/Customers.module.ts b/packages/server/src/modules/Customers/Customers.module.ts index 6d192f9ae..c63260d0a 100644 --- a/packages/server/src/modules/Customers/Customers.module.ts +++ b/packages/server/src/modules/Customers/Customers.module.ts @@ -25,7 +25,12 @@ import { CustomerGLEntriesStorage } from './CustomerGLEntriesStorage'; import { CustomerWriteGLOpeningBalanceSubscriber } from './subscribers/CustomerGLEntriesSubscriber'; @Module({ - imports: [TenancyDatabaseModule, DynamicListModule, LedgerModule, AccountsModule], + imports: [ + TenancyDatabaseModule, + DynamicListModule, + LedgerModule, + AccountsModule, + ], controllers: [CustomersController], providers: [ ActivateCustomer, @@ -51,4 +56,4 @@ import { CustomerWriteGLOpeningBalanceSubscriber } from './subscribers/CustomerG CustomerWriteGLOpeningBalanceSubscriber, ], }) -export class CustomersModule { } +export class CustomersModule {} diff --git a/packages/server/src/modules/Customers/CustomersApplication.service.ts b/packages/server/src/modules/Customers/CustomersApplication.service.ts index 74923ff71..f1559b550 100644 --- a/packages/server/src/modules/Customers/CustomersApplication.service.ts +++ b/packages/server/src/modules/Customers/CustomersApplication.service.ts @@ -4,10 +4,7 @@ import { CreateCustomer } from './commands/CreateCustomer.service'; import { EditCustomer } from './commands/EditCustomer.service'; import { DeleteCustomer } from './commands/DeleteCustomer.service'; import { EditOpeningBalanceCustomer } from './commands/EditOpeningBalanceCustomer.service'; -import { - ICustomerOpeningBalanceEditDTO, - ICustomersFilter, -} from './types/Customers.types'; +import { CustomerOpeningBalanceEditDto } from './dtos/CustomerOpeningBalanceEdit.dto'; import { CreateCustomerDto } from './dtos/CreateCustomer.dto'; import { EditCustomerDto } from './dtos/EditCustomer.dto'; import { GetCustomers } from './queries/GetCustomers.service'; @@ -18,12 +15,12 @@ import { ValidateBulkDeleteCustomersService } from './ValidateBulkDeleteCustomer @Injectable() export class CustomersApplication { constructor( - private getCustomerService: GetCustomerService, - private createCustomerService: CreateCustomer, - private editCustomerService: EditCustomer, - private deleteCustomerService: DeleteCustomer, - private editOpeningBalanceService: EditOpeningBalanceCustomer, - private getCustomersService: GetCustomers, + private readonly getCustomerService: GetCustomerService, + private readonly createCustomerService: CreateCustomer, + private readonly editCustomerService: EditCustomer, + private readonly deleteCustomerService: DeleteCustomer, + private readonly editOpeningBalanceService: EditOpeningBalanceCustomer, + private readonly getCustomersService: GetCustomers, private readonly bulkDeleteCustomersService: BulkDeleteCustomersService, private readonly validateBulkDeleteCustomersService: ValidateBulkDeleteCustomersService, ) {} @@ -72,7 +69,7 @@ export class CustomersApplication { */ public editOpeningBalance = ( customerId: number, - openingBalanceEditDTO: ICustomerOpeningBalanceEditDTO, + openingBalanceEditDTO: CustomerOpeningBalanceEditDto, ) => { return this.editOpeningBalanceService.changeOpeningBalance( customerId, @@ -82,7 +79,7 @@ export class CustomersApplication { /** * Retrieve customers paginated list. - * @param {ICustomersFilter} filter - Cusotmers filter. + * @param {GetCustomersQueryDto} filter - Cusotmers filter. */ public getCustomers = (filterDTO: GetCustomersQueryDto) => { return this.getCustomersService.getCustomersList(filterDTO); diff --git a/packages/server/src/modules/Customers/commands/CreateCustomer.service.ts b/packages/server/src/modules/Customers/commands/CreateCustomer.service.ts index 145656519..246c02548 100644 --- a/packages/server/src/modules/Customers/commands/CreateCustomer.service.ts +++ b/packages/server/src/modules/Customers/commands/CreateCustomer.service.ts @@ -31,7 +31,7 @@ export class CreateCustomer { /** * Creates a new customer. - * @param {ICustomerNewDTO} customerDTO + * @param {CreateCustomerDto} customerDTO * @return {Promise} */ public async createCustomer( diff --git a/packages/server/src/modules/Customers/commands/EditOpeningBalanceCustomer.service.ts b/packages/server/src/modules/Customers/commands/EditOpeningBalanceCustomer.service.ts index b58165ab6..33606d7d5 100644 --- a/packages/server/src/modules/Customers/commands/EditOpeningBalanceCustomer.service.ts +++ b/packages/server/src/modules/Customers/commands/EditOpeningBalanceCustomer.service.ts @@ -1,10 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { Knex } from 'knex'; import { - ICustomerOpeningBalanceEditDTO, ICustomerOpeningBalanceEditedPayload, ICustomerOpeningBalanceEditingPayload, } from '../types/Customers.types'; +import { CustomerOpeningBalanceEditDto } from '../dtos/CustomerOpeningBalanceEdit.dto'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service'; import { Customer } from '../models/Customer'; @@ -29,11 +29,11 @@ export class EditOpeningBalanceCustomer { /** * Changes the opening balance of the given customer. * @param {number} customerId - Customer ID. - * @param {ICustomerOpeningBalanceEditDTO} openingBalanceEditDTO + * @param {CustomerOpeningBalanceEditDto} openingBalanceEditDTO */ public async changeOpeningBalance( customerId: number, - openingBalanceEditDTO: ICustomerOpeningBalanceEditDTO, + openingBalanceEditDTO: CustomerOpeningBalanceEditDto, ): Promise { // Retrieves the old customer or throw not found error. const oldCustomer = await this.customerModel() diff --git a/packages/server/src/modules/Customers/dtos/CreateCustomer.dto.ts b/packages/server/src/modules/Customers/dtos/CreateCustomer.dto.ts index bad384eaf..9569d9da4 100644 --- a/packages/server/src/modules/Customers/dtos/CreateCustomer.dto.ts +++ b/packages/server/src/modules/Customers/dtos/CreateCustomer.dto.ts @@ -4,6 +4,7 @@ import { IsNotEmpty, IsNumber, IsString, + ValidateIf, } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; import { IsOptional, ToNumber } from '@/common/decorators/Validators'; @@ -40,10 +41,11 @@ export class CreateCustomerDto extends ContactAddressDto { @ApiProperty({ required: false, - description: 'Opening balance date', + description: 'Opening balance date (required when openingBalance is provided)', example: '2024-01-01', }) - @IsOptional() + @ValidateIf((o) => o.openingBalance != null) + @IsNotEmpty({ message: 'openingBalanceAt is required when openingBalance is provided' }) @IsString() openingBalanceAt?: string; diff --git a/packages/server/src/modules/Customers/dtos/CustomerOpeningBalanceEdit.dto.ts b/packages/server/src/modules/Customers/dtos/CustomerOpeningBalanceEdit.dto.ts new file mode 100644 index 000000000..4a762eb18 --- /dev/null +++ b/packages/server/src/modules/Customers/dtos/CustomerOpeningBalanceEdit.dto.ts @@ -0,0 +1,44 @@ +import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsOptional, ToNumber } from '@/common/decorators/Validators'; + +export class CustomerOpeningBalanceEditDto { + @ApiProperty({ + required: true, + description: 'Opening balance', + example: 5000.0, + }) + @IsNumber() + @IsNotEmpty() + @ToNumber() + openingBalance: number; + + @ApiProperty({ + required: false, + description: 'Opening balance date', + example: '2024-01-01', + }) + @IsOptional() + @IsString() + openingBalanceAt?: string; + + @ApiProperty({ + required: false, + description: 'Opening balance exchange rate', + example: 1.0, + }) + @IsOptional() + @IsNumber() + @ToNumber() + openingBalanceExchangeRate?: number; + + @ApiProperty({ + required: false, + description: 'Opening balance branch ID', + example: 101, + }) + @IsOptional() + @IsNumber() + @ToNumber() + openingBalanceBranchId?: number; +} diff --git a/packages/server/src/modules/Customers/types/Customers.types.ts b/packages/server/src/modules/Customers/types/Customers.types.ts index 0ff4bd00f..5bb0b85f2 100644 --- a/packages/server/src/modules/Customers/types/Customers.types.ts +++ b/packages/server/src/modules/Customers/types/Customers.types.ts @@ -4,6 +4,7 @@ import { IContactAddressDTO } from '@/modules/Contacts/types/Contacts.types'; import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types'; import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model'; import { CreateCustomerDto } from '../dtos/CreateCustomer.dto'; +import { CustomerOpeningBalanceEditDto } from '../dtos/CustomerOpeningBalanceEdit.dto'; import { EditCustomerDto } from '../dtos/EditCustomer.dto'; // Customer Interfaces. @@ -113,23 +114,16 @@ export enum VendorAction { View = 'View', } -export interface ICustomerOpeningBalanceEditDTO { - openingBalance: number; - openingBalanceAt: Date | string; - openingBalanceExchangeRate: number; - openingBalanceBranchId?: number; -} - export interface ICustomerOpeningBalanceEditingPayload { oldCustomer: Customer; - openingBalanceEditDTO: ICustomerOpeningBalanceEditDTO; + openingBalanceEditDTO: CustomerOpeningBalanceEditDto; trx?: Knex.Transaction; } export interface ICustomerOpeningBalanceEditedPayload { customer: Customer; oldCustomer: Customer; - openingBalanceEditDTO: ICustomerOpeningBalanceEditDTO; + openingBalanceEditDTO: CustomerOpeningBalanceEditDto; trx: Knex.Transaction; } diff --git a/packages/server/src/modules/Vendors/Vendors.controller.ts b/packages/server/src/modules/Vendors/Vendors.controller.ts index 25d105f03..f2d212b8b 100644 --- a/packages/server/src/modules/Vendors/Vendors.controller.ts +++ b/packages/server/src/modules/Vendors/Vendors.controller.ts @@ -9,10 +9,7 @@ import { Query, } from '@nestjs/common'; import { VendorsApplication } from './VendorsApplication.service'; -import { - IVendorOpeningBalanceEditDTO, - IVendorsFilter, -} from './types/Vendors.types'; +import { VendorOpeningBalanceEditDto } from './dtos/VendorOpeningBalanceEdit.dto'; import { ApiOperation, ApiResponse, @@ -68,7 +65,7 @@ export class VendorsController { @ApiOperation({ summary: 'Edit the given vendor opening balance.' }) editOpeningBalance( @Param('id') vendorId: number, - @Body() openingBalanceDTO: IVendorOpeningBalanceEditDTO, + @Body() openingBalanceDTO: VendorOpeningBalanceEditDto, ) { return this.vendorsApplication.editOpeningBalance( vendorId, diff --git a/packages/server/src/modules/Vendors/VendorsApplication.service.ts b/packages/server/src/modules/Vendors/VendorsApplication.service.ts index 69e7afd34..1d2549152 100644 --- a/packages/server/src/modules/Vendors/VendorsApplication.service.ts +++ b/packages/server/src/modules/Vendors/VendorsApplication.service.ts @@ -5,10 +5,7 @@ import { EditVendorService } from './commands/EditVendor.service'; import { DeleteVendorService } from './commands/DeleteVendor.service'; import { EditOpeningBalanceVendorService } from './commands/EditOpeningBalanceVendor.service'; import { GetVendorService } from './queries/GetVendor'; -import { - IVendorOpeningBalanceEditDTO, - IVendorsFilter, -} from './types/Vendors.types'; +import { VendorOpeningBalanceEditDto } from './dtos/VendorOpeningBalanceEdit.dto'; import { GetVendorsService } from './queries/GetVendors.service'; import { CreateVendorDto } from './dtos/CreateVendor.dto'; import { EditVendorDto } from './dtos/EditVendor.dto'; @@ -58,14 +55,14 @@ export class VendorsApplication { } /** - * Changes the opening balance of the given customer. - * @param {number} vendorId - * @param {IVendorOpeningBalanceEditDTO} openingBalanceEditDTO + * Changes the opening balance of the given vendor. + * @param {number} vendorId + * @param {VendorOpeningBalanceEditDto} openingBalanceEditDTO * @returns {Promise} */ public editOpeningBalance( vendorId: number, - openingBalanceEditDTO: IVendorOpeningBalanceEditDTO, + openingBalanceEditDTO: VendorOpeningBalanceEditDto, ) { return this.editOpeningBalanceService.editOpeningBalance( vendorId, @@ -95,10 +92,7 @@ export class VendorsApplication { vendorIds: number[], options?: { skipUndeletable?: boolean }, ) { - return this.bulkDeleteVendorsService.bulkDeleteVendors( - vendorIds, - options, - ); + return this.bulkDeleteVendorsService.bulkDeleteVendors(vendorIds, options); } public validateBulkDeleteVendors(vendorIds: number[]) { diff --git a/packages/server/src/modules/Vendors/commands/EditOpeningBalanceVendor.service.ts b/packages/server/src/modules/Vendors/commands/EditOpeningBalanceVendor.service.ts index 41ccdad0d..c15222ada 100644 --- a/packages/server/src/modules/Vendors/commands/EditOpeningBalanceVendor.service.ts +++ b/packages/server/src/modules/Vendors/commands/EditOpeningBalanceVendor.service.ts @@ -2,10 +2,10 @@ import { Knex } from 'knex'; import { Inject, Injectable } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { - IVendorOpeningBalanceEditDTO, IVendorOpeningBalanceEditedPayload, IVendorOpeningBalanceEditingPayload, } from '../types/Vendors.types'; +import { VendorOpeningBalanceEditDto } from '../dtos/VendorOpeningBalanceEdit.dto'; import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service'; import { Vendor } from '../models/Vendor'; import { events } from '@/common/events/events'; @@ -29,12 +29,12 @@ export class EditOpeningBalanceVendorService { /** * Changes the opening balance of the given customer. * @param {number} vendorId - * @param {IVendorOpeningBalanceEditDTO} openingBalanceEditDTO + * @param {VendorOpeningBalanceEditDto} openingBalanceEditDTO * @returns {Promise} */ public async editOpeningBalance( vendorId: number, - openingBalanceEditDTO: IVendorOpeningBalanceEditDTO, + openingBalanceEditDTO: VendorOpeningBalanceEditDto, ) { // Retrieves the old vendor or throw not found error. const oldVendor = await this.vendorModel() diff --git a/packages/server/src/modules/Vendors/dtos/CreateVendor.dto.ts b/packages/server/src/modules/Vendors/dtos/CreateVendor.dto.ts index e79951ec2..65cf9fe4e 100644 --- a/packages/server/src/modules/Vendors/dtos/CreateVendor.dto.ts +++ b/packages/server/src/modules/Vendors/dtos/CreateVendor.dto.ts @@ -2,11 +2,13 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsISO8601, IsInt, + IsNotEmpty, IsNumber, Min, IsBoolean, IsEmail, IsString, + ValidateIf, } from 'class-validator'; import { ContactAddressDto } from '@/modules/Customers/dtos/ContactAddress.dto'; import { IsOptional, ToNumber } from '@/common/decorators/Validators'; @@ -30,8 +32,12 @@ export class CreateVendorDto extends ContactAddressDto { @ToNumber() openingBalanceExchangeRate?: number; - @ApiProperty({ required: false, description: 'Date of the opening balance' }) - @IsOptional() + @ApiProperty({ + required: false, + description: 'Date of the opening balance (required when openingBalance is provided)', + }) + @ValidateIf((o) => o.openingBalance != null) + @IsNotEmpty({ message: 'openingBalanceAt is required when openingBalance is provided' }) @IsISO8601() openingBalanceAt?: Date; diff --git a/packages/server/src/modules/Vendors/dtos/VendorOpeningBalanceEdit.dto.ts b/packages/server/src/modules/Vendors/dtos/VendorOpeningBalanceEdit.dto.ts new file mode 100644 index 000000000..47e6004cf --- /dev/null +++ b/packages/server/src/modules/Vendors/dtos/VendorOpeningBalanceEdit.dto.ts @@ -0,0 +1,44 @@ +import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsOptional, ToNumber } from '@/common/decorators/Validators'; + +export class VendorOpeningBalanceEditDto { + @ApiProperty({ + required: true, + description: 'Opening balance', + example: 5000.0, + }) + @IsNumber() + @IsNotEmpty() + @ToNumber() + openingBalance: number; + + @ApiProperty({ + required: false, + description: 'Opening balance date', + example: '2024-01-01', + }) + @IsOptional() + @IsString() + openingBalanceAt?: string; + + @ApiProperty({ + required: false, + description: 'Opening balance exchange rate', + example: 1.0, + }) + @IsOptional() + @IsNumber() + @ToNumber() + openingBalanceExchangeRate?: number; + + @ApiProperty({ + required: false, + description: 'Opening balance branch ID', + example: 101, + }) + @IsOptional() + @IsNumber() + @ToNumber() + openingBalanceBranchId?: number; +} diff --git a/packages/server/src/modules/Vendors/types/Vendors.types.ts b/packages/server/src/modules/Vendors/types/Vendors.types.ts index 4357bb7b4..381e3abc9 100644 --- a/packages/server/src/modules/Vendors/types/Vendors.types.ts +++ b/packages/server/src/modules/Vendors/types/Vendors.types.ts @@ -7,6 +7,7 @@ import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/Dynam import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model'; import { CreateVendorDto } from '../dtos/CreateVendor.dto'; import { EditVendorDto } from '../dtos/EditVendor.dto'; +import { VendorOpeningBalanceEditDto } from '../dtos/VendorOpeningBalanceEdit.dto'; // ---------------------------------- export interface IVendorNewDTO extends IContactAddressDTO { @@ -92,23 +93,16 @@ export interface IVendorEventEditedPayload { trx?: Knex.Transaction; } -export interface IVendorOpeningBalanceEditDTO { - openingBalance: number; - openingBalanceAt: Date | string; - openingBalanceExchangeRate: number; - openingBalanceBranchId?: number; -} - export interface IVendorOpeningBalanceEditingPayload { oldVendor: Vendor; - openingBalanceEditDTO: IVendorOpeningBalanceEditDTO; + openingBalanceEditDTO: VendorOpeningBalanceEditDto; trx?: Knex.Transaction; } export interface IVendorOpeningBalanceEditedPayload { vendor: Vendor; oldVendor: Vendor; - openingBalanceEditDTO: IVendorOpeningBalanceEditDTO; + openingBalanceEditDTO: VendorOpeningBalanceEditDto; trx: Knex.Transaction; }