Compare commits

...

2 Commits

Author SHA1 Message Date
Ahmed Bouhuolia
04d065b969 wip 2026-01-24 13:59:43 +02:00
Ahmed Bouhuolia
ca910ee489 fix(server): customer/vendor opening balance: 2026-01-23 17:43:22 +02:00
36 changed files with 661 additions and 639 deletions

View File

@@ -17,7 +17,7 @@ export class BillBranchValidateSubscriber {
* Validate branch existance on bill creating. * Validate branch existance on bill creating.
* @param {IBillCreatingPayload} payload * @param {IBillCreatingPayload} payload
*/ */
@OnEvent(events.bill.onCreating) @OnEvent(events.bill.onCreating, { suppressErrors: false })
async validateBranchExistanceOnBillCreating({ async validateBranchExistanceOnBillCreating({
billDTO, billDTO,
}: IBillCreatingPayload) { }: IBillCreatingPayload) {
@@ -30,7 +30,7 @@ export class BillBranchValidateSubscriber {
* Validate branch existance once bill editing. * Validate branch existance once bill editing.
* @param {IBillEditingPayload} payload * @param {IBillEditingPayload} payload
*/ */
@OnEvent(events.bill.onEditing) @OnEvent(events.bill.onEditing, { suppressErrors: false })
async validateBranchExistanceOnBillEditing({ billDTO }: IBillEditingPayload) { async validateBranchExistanceOnBillEditing({ billDTO }: IBillEditingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive( await this.validateBranchExistance.validateTransactionBranchWhenActive(
billDTO.branchId, billDTO.branchId,

View File

@@ -14,7 +14,7 @@ export class CashflowBranchDTOValidatorSubscriber {
* Validate branch existance once cashflow transaction creating. * Validate branch existance once cashflow transaction creating.
* @param {ICommandCashflowCreatingPayload} payload * @param {ICommandCashflowCreatingPayload} payload
*/ */
@OnEvent(events.cashflow.onTransactionCreating) @OnEvent(events.cashflow.onTransactionCreating, { suppressErrors: false })
async validateBranchExistanceOnCashflowTransactionCreating({ async validateBranchExistanceOnCashflowTransactionCreating({
newTransactionDTO, newTransactionDTO,
}: ICommandCashflowCreatingPayload) { }: ICommandCashflowCreatingPayload) {

View File

@@ -15,13 +15,13 @@ import {
export class ContactBranchValidateSubscriber { export class ContactBranchValidateSubscriber {
constructor( constructor(
private readonly validateBranchExistance: ValidateBranchExistance, private readonly validateBranchExistance: ValidateBranchExistance,
) { } ) {}
/** /**
* Validate branch existance on customer creating. * Validate branch existance on customer creating.
* @param {ICustomerEventCreatingPayload} payload * @param {ICustomerEventCreatingPayload} payload
*/ */
@OnEvent(events.customers.onCreating) @OnEvent(events.customers.onCreating, { suppressErrors: false })
async validateBranchExistanceOnCustomerCreating({ async validateBranchExistanceOnCustomerCreating({
customerDTO, customerDTO,
}: ICustomerEventCreatingPayload) { }: ICustomerEventCreatingPayload) {
@@ -37,7 +37,7 @@ export class ContactBranchValidateSubscriber {
* Validate branch existance once customer opening balance editing. * Validate branch existance once customer opening balance editing.
* @param {ICustomerOpeningBalanceEditingPayload} payload * @param {ICustomerOpeningBalanceEditingPayload} payload
*/ */
@OnEvent(events.customers.onOpeningBalanceChanging) @OnEvent(events.customers.onOpeningBalanceChanging, { suppressErrors: false })
async validateBranchExistanceOnCustomerOpeningBalanceEditing({ async validateBranchExistanceOnCustomerOpeningBalanceEditing({
openingBalanceEditDTO, openingBalanceEditDTO,
}: ICustomerOpeningBalanceEditingPayload) { }: ICustomerOpeningBalanceEditingPayload) {
@@ -52,7 +52,7 @@ export class ContactBranchValidateSubscriber {
* Validates the branch existance on vendor creating. * Validates the branch existance on vendor creating.
* @param {IVendorEventCreatingPayload} payload * @param {IVendorEventCreatingPayload} payload
*/ */
@OnEvent(events.vendors.onCreating) @OnEvent(events.vendors.onCreating, { suppressErrors: false })
async validateBranchExistanceonVendorCreating({ async validateBranchExistanceonVendorCreating({
vendorDTO, vendorDTO,
}: IVendorEventCreatingPayload) { }: IVendorEventCreatingPayload) {
@@ -68,7 +68,7 @@ export class ContactBranchValidateSubscriber {
* Validate branch existance once the vendor opening balance editing. * Validate branch existance once the vendor opening balance editing.
* @param {IVendorOpeningBalanceEditingPayload} payload * @param {IVendorOpeningBalanceEditingPayload} payload
*/ */
@OnEvent(events.vendors.onOpeningBalanceChanging) @OnEvent(events.vendors.onOpeningBalanceChanging, { suppressErrors: false })
async validateBranchExistanceOnVendorOpeningBalanceEditing({ async validateBranchExistanceOnVendorOpeningBalanceEditing({
openingBalanceEditDTO, openingBalanceEditDTO,
}: IVendorOpeningBalanceEditingPayload) { }: IVendorOpeningBalanceEditingPayload) {

View File

@@ -15,7 +15,7 @@ export class CreditNoteBranchValidateSubscriber {
* Validate branch existance on credit note creating. * Validate branch existance on credit note creating.
* @param {ICreditNoteCreatingPayload} payload * @param {ICreditNoteCreatingPayload} payload
*/ */
@OnEvent(events.creditNote.onCreating) @OnEvent(events.creditNote.onCreating, { suppressErrors: false })
async validateBranchExistanceOnCreditCreating({ async validateBranchExistanceOnCreditCreating({
creditNoteDTO, creditNoteDTO,
}: ICreditNoteCreatingPayload) { }: ICreditNoteCreatingPayload) {
@@ -28,7 +28,7 @@ export class CreditNoteBranchValidateSubscriber {
* Validate branch existance once credit note editing. * Validate branch existance once credit note editing.
* @param {ICreditNoteEditingPayload} payload * @param {ICreditNoteEditingPayload} payload
*/ */
@OnEvent(events.creditNote.onEditing) @OnEvent(events.creditNote.onEditing, { suppressErrors: false })
async validateBranchExistanceOnCreditEditing({ async validateBranchExistanceOnCreditEditing({
creditNoteEditDTO, creditNoteEditDTO,
}: ICreditNoteEditingPayload) { }: ICreditNoteEditingPayload) {

View File

@@ -14,7 +14,7 @@ export class CreditNoteRefundBranchValidateSubscriber {
* Validate branch existance on refund credit note creating. * Validate branch existance on refund credit note creating.
* @param {IRefundCreditNoteCreatingPayload} payload * @param {IRefundCreditNoteCreatingPayload} payload
*/ */
@OnEvent(events.creditNote.onRefundCreating) @OnEvent(events.creditNote.onRefundCreating, { suppressErrors: false })
async validateBranchExistanceOnCreditRefundCreating({ async validateBranchExistanceOnCreditRefundCreating({
newCreditNoteDTO, newCreditNoteDTO,
}: IRefundCreditNoteCreatingPayload) { }: IRefundCreditNoteCreatingPayload) {

View File

@@ -16,7 +16,7 @@ export class ExpenseBranchValidateSubscriber {
* Validate branch existance once expense transaction creating. * Validate branch existance once expense transaction creating.
* @param {IExpenseCreatingPayload} payload * @param {IExpenseCreatingPayload} payload
*/ */
@OnEvent(events.expenses.onCreating) @OnEvent(events.expenses.onCreating, { suppressErrors: false })
async validateBranchExistanceOnExpenseCreating({ async validateBranchExistanceOnExpenseCreating({
expenseDTO, expenseDTO,
}: IExpenseCreatingPayload) { }: IExpenseCreatingPayload) {
@@ -29,7 +29,7 @@ export class ExpenseBranchValidateSubscriber {
* Validate branch existance once expense transaction editing. * Validate branch existance once expense transaction editing.
* @param {IExpenseEventEditingPayload} payload * @param {IExpenseEventEditingPayload} payload
*/ */
@OnEvent(events.expenses.onEditing) @OnEvent(events.expenses.onEditing, { suppressErrors: false })
async validateBranchExistanceOnExpenseEditing({ async validateBranchExistanceOnExpenseEditing({
expenseDTO, expenseDTO,
}: IExpenseEventEditingPayload) { }: IExpenseEventEditingPayload) {

View File

@@ -14,7 +14,7 @@ export class InventoryAdjustmentBranchValidateSubscriber {
* Validate branch existance on inventory adjustment creating. * Validate branch existance on inventory adjustment creating.
* @param {IInventoryAdjustmentCreatingPayload} payload * @param {IInventoryAdjustmentCreatingPayload} payload
*/ */
@OnEvent(events.inventoryAdjustment.onQuickCreating) @OnEvent(events.inventoryAdjustment.onQuickCreating, { suppressErrors: false })
async validateBranchExistanceOnInventoryCreating({ async validateBranchExistanceOnInventoryCreating({
quickAdjustmentDTO, quickAdjustmentDTO,
}: IInventoryAdjustmentCreatingPayload) { }: IInventoryAdjustmentCreatingPayload) {

View File

@@ -17,7 +17,7 @@ export class InvoiceBranchValidateSubscriber {
* Validate branch existance on invoice creating. * Validate branch existance on invoice creating.
* @param {ISaleInvoiceCreatingPayload} payload * @param {ISaleInvoiceCreatingPayload} payload
*/ */
@OnEvent(events.saleInvoice.onCreating) @OnEvent(events.saleInvoice.onCreating, { suppressErrors: false })
async validateBranchExistanceOnInvoiceCreating({ async validateBranchExistanceOnInvoiceCreating({
saleInvoiceDTO, saleInvoiceDTO,
}: ISaleInvoiceCreatingPaylaod) { }: ISaleInvoiceCreatingPaylaod) {
@@ -30,7 +30,7 @@ export class InvoiceBranchValidateSubscriber {
* Validate branch existance once invoice editing. * Validate branch existance once invoice editing.
* @param {ISaleInvoiceEditingPayload} payload * @param {ISaleInvoiceEditingPayload} payload
*/ */
@OnEvent(events.saleInvoice.onEditing) @OnEvent(events.saleInvoice.onEditing, { suppressErrors: false })
async validateBranchExistanceOnInvoiceEditing({ async validateBranchExistanceOnInvoiceEditing({
saleInvoiceDTO, saleInvoiceDTO,
}: ISaleInvoiceEditingPayload) { }: ISaleInvoiceEditingPayload) {

View File

@@ -17,7 +17,7 @@ export class PaymentMadeBranchValidateSubscriber {
* Validate branch existance on estimate creating. * Validate branch existance on estimate creating.
* @param {ISaleEstimateCreatedPayload} payload * @param {ISaleEstimateCreatedPayload} payload
*/ */
@OnEvent(events.billPayment.onCreating) @OnEvent(events.billPayment.onCreating, { suppressErrors: false })
async validateBranchExistanceOnPaymentCreating({ async validateBranchExistanceOnPaymentCreating({
billPaymentDTO, billPaymentDTO,
}: IBillPaymentCreatingPayload) { }: IBillPaymentCreatingPayload) {
@@ -30,7 +30,7 @@ export class PaymentMadeBranchValidateSubscriber {
* Validate branch existance once estimate editing. * Validate branch existance once estimate editing.
* @param {ISaleEstimateEditingPayload} payload * @param {ISaleEstimateEditingPayload} payload
*/ */
@OnEvent(events.billPayment.onEditing) @OnEvent(events.billPayment.onEditing, { suppressErrors: false })
async validateBranchExistanceOnPaymentEditing({ async validateBranchExistanceOnPaymentEditing({
billPaymentDTO, billPaymentDTO,
}: IBillPaymentEditingPayload) { }: IBillPaymentEditingPayload) {

View File

@@ -17,7 +17,7 @@ export class PaymentReceiveBranchValidateSubscriber {
* Validate branch existance on estimate creating. * Validate branch existance on estimate creating.
* @param {IPaymentReceivedCreatingPayload} payload * @param {IPaymentReceivedCreatingPayload} payload
*/ */
@OnEvent(events.paymentReceive.onCreating) @OnEvent(events.paymentReceive.onCreating, { suppressErrors: false })
async validateBranchExistanceOnPaymentCreating({ async validateBranchExistanceOnPaymentCreating({
paymentReceiveDTO, paymentReceiveDTO,
}: IPaymentReceivedCreatingPayload) { }: IPaymentReceivedCreatingPayload) {
@@ -30,7 +30,7 @@ export class PaymentReceiveBranchValidateSubscriber {
* Validate branch existance once estimate editing. * Validate branch existance once estimate editing.
* @param {IPaymentReceivedEditingPayload} payload * @param {IPaymentReceivedEditingPayload} payload
*/ */
@OnEvent(events.paymentReceive.onEditing) @OnEvent(events.paymentReceive.onEditing, { suppressErrors: false })
async validateBranchExistanceOnPaymentEditing({ async validateBranchExistanceOnPaymentEditing({
paymentReceiveDTO, paymentReceiveDTO,
}: IPaymentReceivedEditingPayload) { }: IPaymentReceivedEditingPayload) {

View File

@@ -17,7 +17,7 @@ export class SaleEstimateBranchValidateSubscriber {
* Validate branch existance on estimate creating. * Validate branch existance on estimate creating.
* @param {ISaleEstimateCreatedPayload} payload * @param {ISaleEstimateCreatedPayload} payload
*/ */
@OnEvent(events.saleEstimate.onCreating) @OnEvent(events.saleEstimate.onCreating, { suppressErrors: false })
async validateBranchExistanceOnEstimateCreating({ async validateBranchExistanceOnEstimateCreating({
estimateDTO, estimateDTO,
}: ISaleEstimateCreatingPayload) { }: ISaleEstimateCreatingPayload) {
@@ -30,7 +30,7 @@ export class SaleEstimateBranchValidateSubscriber {
* Validate branch existance once estimate editing. * Validate branch existance once estimate editing.
* @param {ISaleEstimateEditingPayload} payload * @param {ISaleEstimateEditingPayload} payload
*/ */
@OnEvent(events.saleEstimate.onEditing) @OnEvent(events.saleEstimate.onEditing, { suppressErrors: false })
async validateBranchExistanceOnEstimateEditing({ async validateBranchExistanceOnEstimateEditing({
estimateDTO, estimateDTO,
}: ISaleEstimateEditingPayload) { }: ISaleEstimateEditingPayload) {

View File

@@ -17,7 +17,7 @@ export class SaleReceiptBranchValidateSubscriber {
* Validate branch existance on estimate creating. * Validate branch existance on estimate creating.
* @param {ISaleReceiptCreatingPayload} payload * @param {ISaleReceiptCreatingPayload} payload
*/ */
@OnEvent(events.saleReceipt.onCreating) @OnEvent(events.saleReceipt.onCreating, { suppressErrors: false })
async validateBranchExistanceOnInvoiceCreating({ async validateBranchExistanceOnInvoiceCreating({
saleReceiptDTO, saleReceiptDTO,
}: ISaleReceiptCreatingPayload) { }: ISaleReceiptCreatingPayload) {
@@ -30,7 +30,7 @@ export class SaleReceiptBranchValidateSubscriber {
* Validate branch existance once estimate editing. * Validate branch existance once estimate editing.
* @param {ISaleReceiptEditingPayload} payload * @param {ISaleReceiptEditingPayload} payload
*/ */
@OnEvent(events.saleReceipt.onEditing) @OnEvent(events.saleReceipt.onEditing, { suppressErrors: false })
async validateBranchExistanceOnInvoiceEditing({ async validateBranchExistanceOnInvoiceEditing({
saleReceiptDTO, saleReceiptDTO,
}: ISaleReceiptEditingPayload) { }: ISaleReceiptEditingPayload) {

View File

@@ -17,7 +17,7 @@ export class VendorCreditBranchValidateSubscriber {
* Validate branch existance on estimate creating. * Validate branch existance on estimate creating.
* @param {ISaleEstimateCreatedPayload} payload * @param {ISaleEstimateCreatedPayload} payload
*/ */
@OnEvent(events.vendorCredit.onCreating) @OnEvent(events.vendorCredit.onCreating, { suppressErrors: false })
async validateBranchExistanceOnCreditCreating({ async validateBranchExistanceOnCreditCreating({
vendorCreditCreateDTO, vendorCreditCreateDTO,
}: IVendorCreditCreatingPayload) { }: IVendorCreditCreatingPayload) {
@@ -30,7 +30,7 @@ export class VendorCreditBranchValidateSubscriber {
* Validate branch existance once estimate editing. * Validate branch existance once estimate editing.
* @param {ISaleEstimateEditingPayload} payload * @param {ISaleEstimateEditingPayload} payload
*/ */
@OnEvent(events.vendorCredit.onEditing) @OnEvent(events.vendorCredit.onEditing, { suppressErrors: false })
async validateBranchExistanceOnCreditEditing({ async validateBranchExistanceOnCreditEditing({
vendorCreditDTO, vendorCreditDTO,
}: IVendorCreditEditingPayload) { }: IVendorCreditEditingPayload) {

View File

@@ -14,7 +14,7 @@ export class VendorCreditRefundBranchValidateSubscriber {
* Validate branch existance on refund credit note creating. * Validate branch existance on refund credit note creating.
* @param {IRefundVendorCreditCreatingPayload} payload * @param {IRefundVendorCreditCreatingPayload} payload
*/ */
@OnEvent(events.vendorCredit.onRefundCreating) @OnEvent(events.vendorCredit.onRefundCreating, { suppressErrors: false })
async validateBranchExistanceOnCreditRefundCreating({ async validateBranchExistanceOnCreditRefundCreating({
refundVendorCreditDTO, refundVendorCreditDTO,
}: IRefundVendorCreditCreatingPayload) { }: IRefundVendorCreditCreatingPayload) {

View File

@@ -1,117 +1,103 @@
// import { Service, Inject } from 'typedi'; import { Injectable } from '@nestjs/common';
// import { AccountNormal, ICustomer, ILedgerEntry } from '@/interfaces'; import { AccountNormal } from '@/interfaces/Account';
// import Ledger from '@/services/Accounting/Ledger'; import { ILedgerEntry } from '@/modules/Ledger/types/Ledger.types';
import { Ledger } from '@/modules/Ledger/Ledger';
import { Customer } from './models/Customer';
// @Service() @Injectable()
// export class CustomerGLEntries { export class CustomerGLEntries {
// /** /**
// * Retrieves the customer opening balance common entry attributes. * Retrieves the customer opening balance common entry attributes.
// * @param {ICustomer} customer */
// */ private getCustomerOpeningGLCommonEntry = (customer: Customer) => {
// private getCustomerOpeningGLCommonEntry = (customer: ICustomer) => { return {
// return { exchangeRate: customer.openingBalanceExchangeRate,
// exchangeRate: customer.openingBalanceExchangeRate, currencyCode: customer.currencyCode,
// currencyCode: customer.currencyCode,
// transactionType: 'CustomerOpeningBalance', transactionType: 'CustomerOpeningBalance',
// transactionId: customer.id, transactionId: customer.id,
// date: customer.openingBalanceAt, date: customer.openingBalanceAt,
// userId: customer.userId, contactId: customer.id,
// contactId: customer.id,
// credit: 0, credit: 0,
// debit: 0, debit: 0,
// branchId: customer.openingBalanceBranchId, branchId: customer.openingBalanceBranchId,
// }; };
// }; };
// /** /**
// * Retrieves the customer opening GL credit entry. * Retrieves the customer opening GL credit entry.
// * @param {number} ARAccountId */
// * @param {ICustomer} customer private getCustomerOpeningGLCreditEntry = (
// * @returns {ILedgerEntry} ARAccountId: number,
// */ customer: Customer
// private getCustomerOpeningGLCreditEntry = ( ): ILedgerEntry => {
// ARAccountId: number, const commonEntry = this.getCustomerOpeningGLCommonEntry(customer);
// customer: ICustomer
// ): ILedgerEntry => {
// const commonEntry = this.getCustomerOpeningGLCommonEntry(customer);
// return { return {
// ...commonEntry, ...commonEntry,
// credit: 0, credit: 0,
// debit: customer.localOpeningBalance, debit: customer.localOpeningBalance,
// accountId: ARAccountId, accountId: ARAccountId,
// accountNormal: AccountNormal.DEBIT, accountNormal: AccountNormal.DEBIT,
// index: 1, index: 1,
// }; };
// }; };
// /** /**
// * Retrieves the customer opening GL debit entry. * Retrieves the customer opening GL debit entry.
// * @param {number} incomeAccountId */
// * @param {ICustomer} customer private getCustomerOpeningGLDebitEntry = (
// * @returns {ILedgerEntry} incomeAccountId: number,
// */ customer: Customer
// private getCustomerOpeningGLDebitEntry = ( ): ILedgerEntry => {
// incomeAccountId: number, const commonEntry = this.getCustomerOpeningGLCommonEntry(customer);
// customer: ICustomer
// ): ILedgerEntry => {
// const commonEntry = this.getCustomerOpeningGLCommonEntry(customer);
// return { return {
// ...commonEntry, ...commonEntry,
// credit: customer.localOpeningBalance, credit: customer.localOpeningBalance,
// debit: 0, debit: 0,
// accountId: incomeAccountId, accountId: incomeAccountId,
// accountNormal: AccountNormal.CREDIT, accountNormal: AccountNormal.CREDIT,
// index: 2, index: 2,
// }; };
// }; };
// /** /**
// * Retrieves the customer opening GL entries. * Retrieves the customer opening GL entries.
// * @param {number} ARAccountId */
// * @param {number} incomeAccountId public getCustomerOpeningGLEntries = (
// * @param {ICustomer} customer ARAccountId: number,
// * @returns {ILedgerEntry[]} incomeAccountId: number,
// */ customer: Customer
// public getCustomerOpeningGLEntries = ( ) => {
// ARAccountId: number, const debitEntry = this.getCustomerOpeningGLDebitEntry(
// incomeAccountId: number, incomeAccountId,
// customer: ICustomer customer
// ) => { );
// const debitEntry = this.getCustomerOpeningGLDebitEntry( const creditEntry = this.getCustomerOpeningGLCreditEntry(
// incomeAccountId, ARAccountId,
// customer customer
// ); );
// const creditEntry = this.getCustomerOpeningGLCreditEntry( return [debitEntry, creditEntry];
// ARAccountId, };
// customer
// );
// return [debitEntry, creditEntry];
// };
// /** /**
// * Retrieves the customer opening balance ledger. * Retrieves the customer opening balance ledger.
// * @param {number} ARAccountId */
// * @param {number} incomeAccountId public getCustomerOpeningLedger = (
// * @param {ICustomer} customer ARAccountId: number,
// * @returns {ILedger} incomeAccountId: number,
// */ customer: Customer
// public getCustomerOpeningLedger = ( ) => {
// ARAccountId: number, const entries = this.getCustomerOpeningGLEntries(
// incomeAccountId: number, ARAccountId,
// customer: ICustomer incomeAccountId,
// ) => { customer
// const entries = this.getCustomerOpeningGLEntries( );
// ARAccountId, return new Ledger(entries);
// incomeAccountId, };
// customer }
// );
// return new Ledger(entries);
// };
// }

View File

@@ -1,90 +1,84 @@
// import { Knex } from 'knex'; import { Knex } from 'knex';
// import LedgerStorageService from '@/services/Accounting/LedgerStorageService'; import { Inject, Injectable } from '@nestjs/common';
// import HasTenancyService from '@/services/Tenancy/TenancyService'; import { LedgerStorageService } from '@/modules/Ledger/LedgerStorage.service';
// import { Service, Inject } from 'typedi'; import { AccountRepository } from '@/modules/Accounts/repositories/Account.repository';
// import { CustomerGLEntries } from './CustomerGLEntries'; import { CustomerGLEntries } from './CustomerGLEntries';
import { Customer } from './models/Customer';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { Account } from '../Accounts/models/Account.model';
// @Service() @Injectable()
// export class CustomerGLEntriesStorage { export class CustomerGLEntriesStorage {
// @Inject() constructor(
// private tenancy: HasTenancyService; private readonly ledgerStorage: LedgerStorageService,
private readonly accountRepository: AccountRepository,
private readonly customerGLEntries: CustomerGLEntries,
// @Inject() @Inject(Account.name)
// private ledegrRepository: LedgerStorageService; private readonly accountModel: TenantModelProxy<typeof Account>,
// @Inject() @Inject(Customer.name)
// private customerGLEntries: CustomerGLEntries; private readonly customerModel: TenantModelProxy<typeof Customer>,
) { }
// /** /**
// * Customer opening balance journals. * Customer opening balance journals.
// * @param {number} tenantId */
// * @param {number} customerId public writeCustomerOpeningBalance = async (
// * @param {Knex.Transaction} trx customerId: number,
// */ trx?: Knex.Transaction,
// public writeCustomerOpeningBalance = async ( ) => {
// tenantId: number, const customer = await this.customerModel()
// customerId: number, .query(trx)
// trx?: Knex.Transaction .findById(customerId);
// ) => {
// const { Customer } = this.tenancy.models(tenantId);
// const { accountRepository } = this.tenancy.repositories(tenantId);
// const customer = await Customer.query(trx).findById(customerId); // Finds the income account.
const incomeAccount = await this.accountModel()
.query(trx)
.findOne({ slug: 'other-income' });
// // Finds the income account. // Find or create the A/R account.
// const incomeAccount = await accountRepository.findOne({ const ARAccount =
// slug: 'other-income', await this.accountRepository.findOrCreateAccountReceivable(
// }); customer.currencyCode,
// // Find or create the A/R account. {},
// const ARAccount = await accountRepository.findOrCreateAccountReceivable( trx,
// customer.currencyCode, );
// {}, // Retrieves the customer opening balance ledger.
// trx const ledger = this.customerGLEntries.getCustomerOpeningLedger(
// ); ARAccount.id,
// // Retrieves the customer opening balance ledger. incomeAccount.id,
// const ledger = this.customerGLEntries.getCustomerOpeningLedger( customer,
// ARAccount.id, );
// incomeAccount.id, // Commits the ledger entries to the storage.
// customer await this.ledgerStorage.commit(ledger, trx);
// ); };
// // Commits the ledger entries to the storage.
// await this.ledegrRepository.commit(tenantId, ledger, trx);
// };
// /** /**
// * Reverts the customer opening balance GL entries. * Reverts the customer opening balance GL entries.
// * @param {number} tenantId */
// * @param {number} customerId public revertCustomerOpeningBalance = async (
// * @param {Knex.Transaction} trx customerId: number,
// */ trx?: Knex.Transaction,
// public revertCustomerOpeningBalance = async ( ) => {
// tenantId: number, await this.ledgerStorage.deleteByReference(
// customerId: number, customerId,
// trx?: Knex.Transaction 'CustomerOpeningBalance',
// ) => { trx,
// await this.ledegrRepository.deleteByReference( );
// tenantId, };
// customerId,
// 'CustomerOpeningBalance',
// trx
// );
// };
// /** /**
// * Writes the customer opening balance GL entries. * Writes the customer opening balance GL entries.
// * @param {number} tenantId */
// * @param {number} customerId public rewriteCustomerOpeningBalance = async (
// * @param {Knex.Transaction} trx customerId: number,
// */ trx?: Knex.Transaction,
// public rewriteCustomerOpeningBalance = async ( ) => {
// tenantId: number, // Reverts the customer opening balance entries.
// customerId: number, await this.revertCustomerOpeningBalance(customerId, trx);
// trx?: Knex.Transaction
// ) => {
// // Reverts the customer opening balance entries.
// await this.revertCustomerOpeningBalance(tenantId, customerId, trx);
// // Write the customer opening balance entries. // Write the customer opening balance entries.
// await this.writeCustomerOpeningBalance(tenantId, customerId, trx); await this.writeCustomerOpeningBalance(customerId, trx);
// }; };
// } }

View File

@@ -9,10 +9,7 @@ import {
Query, Query,
} from '@nestjs/common'; } from '@nestjs/common';
import { CustomersApplication } from './CustomersApplication.service'; import { CustomersApplication } from './CustomersApplication.service';
import { import { CustomerOpeningBalanceEditDto } from './dtos/CustomerOpeningBalanceEdit.dto';
ICustomerOpeningBalanceEditDTO,
ICustomersFilter,
} from './types/Customers.types';
import { import {
ApiOperation, ApiOperation,
ApiResponse, ApiResponse,
@@ -106,7 +103,7 @@ export class CustomersController {
}) })
editOpeningBalance( editOpeningBalance(
@Param('id') customerId: number, @Param('id') customerId: number,
@Body() openingBalanceDTO: ICustomerOpeningBalanceEditDTO, @Body() openingBalanceDTO: CustomerOpeningBalanceEditDto,
) { ) {
return this.customersApplication.editOpeningBalance( return this.customersApplication.editOpeningBalance(
customerId, customerId,

View File

@@ -18,9 +18,19 @@ import { GetCustomers } from './queries/GetCustomers.service';
import { DynamicListModule } from '../DynamicListing/DynamicList.module'; import { DynamicListModule } from '../DynamicListing/DynamicList.module';
import { BulkDeleteCustomersService } from './BulkDeleteCustomers.service'; import { BulkDeleteCustomersService } from './BulkDeleteCustomers.service';
import { ValidateBulkDeleteCustomersService } from './ValidateBulkDeleteCustomers.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({ @Module({
imports: [TenancyDatabaseModule, DynamicListModule], imports: [
TenancyDatabaseModule,
DynamicListModule,
LedgerModule,
AccountsModule,
],
controllers: [CustomersController], controllers: [CustomersController],
providers: [ providers: [
ActivateCustomer, ActivateCustomer,
@@ -41,6 +51,9 @@ import { ValidateBulkDeleteCustomersService } from './ValidateBulkDeleteCustomer
GetCustomers, GetCustomers,
BulkDeleteCustomersService, BulkDeleteCustomersService,
ValidateBulkDeleteCustomersService, ValidateBulkDeleteCustomersService,
CustomerGLEntries,
CustomerGLEntriesStorage,
CustomerWriteGLOpeningBalanceSubscriber,
], ],
}) })
export class CustomersModule {} export class CustomersModule {}

View File

@@ -4,10 +4,7 @@ import { CreateCustomer } from './commands/CreateCustomer.service';
import { EditCustomer } from './commands/EditCustomer.service'; import { EditCustomer } from './commands/EditCustomer.service';
import { DeleteCustomer } from './commands/DeleteCustomer.service'; import { DeleteCustomer } from './commands/DeleteCustomer.service';
import { EditOpeningBalanceCustomer } from './commands/EditOpeningBalanceCustomer.service'; import { EditOpeningBalanceCustomer } from './commands/EditOpeningBalanceCustomer.service';
import { import { CustomerOpeningBalanceEditDto } from './dtos/CustomerOpeningBalanceEdit.dto';
ICustomerOpeningBalanceEditDTO,
ICustomersFilter,
} from './types/Customers.types';
import { CreateCustomerDto } from './dtos/CreateCustomer.dto'; import { CreateCustomerDto } from './dtos/CreateCustomer.dto';
import { EditCustomerDto } from './dtos/EditCustomer.dto'; import { EditCustomerDto } from './dtos/EditCustomer.dto';
import { GetCustomers } from './queries/GetCustomers.service'; import { GetCustomers } from './queries/GetCustomers.service';
@@ -18,12 +15,12 @@ import { ValidateBulkDeleteCustomersService } from './ValidateBulkDeleteCustomer
@Injectable() @Injectable()
export class CustomersApplication { export class CustomersApplication {
constructor( constructor(
private getCustomerService: GetCustomerService, private readonly getCustomerService: GetCustomerService,
private createCustomerService: CreateCustomer, private readonly createCustomerService: CreateCustomer,
private editCustomerService: EditCustomer, private readonly editCustomerService: EditCustomer,
private deleteCustomerService: DeleteCustomer, private readonly deleteCustomerService: DeleteCustomer,
private editOpeningBalanceService: EditOpeningBalanceCustomer, private readonly editOpeningBalanceService: EditOpeningBalanceCustomer,
private getCustomersService: GetCustomers, private readonly getCustomersService: GetCustomers,
private readonly bulkDeleteCustomersService: BulkDeleteCustomersService, private readonly bulkDeleteCustomersService: BulkDeleteCustomersService,
private readonly validateBulkDeleteCustomersService: ValidateBulkDeleteCustomersService, private readonly validateBulkDeleteCustomersService: ValidateBulkDeleteCustomersService,
) {} ) {}
@@ -72,7 +69,7 @@ export class CustomersApplication {
*/ */
public editOpeningBalance = ( public editOpeningBalance = (
customerId: number, customerId: number,
openingBalanceEditDTO: ICustomerOpeningBalanceEditDTO, openingBalanceEditDTO: CustomerOpeningBalanceEditDto,
) => { ) => {
return this.editOpeningBalanceService.changeOpeningBalance( return this.editOpeningBalanceService.changeOpeningBalance(
customerId, customerId,
@@ -82,7 +79,7 @@ export class CustomersApplication {
/** /**
* Retrieve customers paginated list. * Retrieve customers paginated list.
* @param {ICustomersFilter} filter - Cusotmers filter. * @param {GetCustomersQueryDto} filter - Cusotmers filter.
*/ */
public getCustomers = (filterDTO: GetCustomersQueryDto) => { public getCustomers = (filterDTO: GetCustomersQueryDto) => {
return this.getCustomersService.getCustomersList(filterDTO); return this.getCustomersService.getCustomersList(filterDTO);

View File

@@ -31,7 +31,7 @@ export class CreateCustomer {
/** /**
* Creates a new customer. * Creates a new customer.
* @param {ICustomerNewDTO} customerDTO * @param {CreateCustomerDto} customerDTO
* @return {Promise<ICustomer>} * @return {Promise<ICustomer>}
*/ */
public async createCustomer( public async createCustomer(

View File

@@ -1,10 +1,10 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { import {
ICustomerOpeningBalanceEditDTO,
ICustomerOpeningBalanceEditedPayload, ICustomerOpeningBalanceEditedPayload,
ICustomerOpeningBalanceEditingPayload, ICustomerOpeningBalanceEditingPayload,
} from '../types/Customers.types'; } from '../types/Customers.types';
import { CustomerOpeningBalanceEditDto } from '../dtos/CustomerOpeningBalanceEdit.dto';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service'; import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { Customer } from '../models/Customer'; import { Customer } from '../models/Customer';
@@ -29,11 +29,11 @@ export class EditOpeningBalanceCustomer {
/** /**
* Changes the opening balance of the given customer. * Changes the opening balance of the given customer.
* @param {number} customerId - Customer ID. * @param {number} customerId - Customer ID.
* @param {ICustomerOpeningBalanceEditDTO} openingBalanceEditDTO * @param {CustomerOpeningBalanceEditDto} openingBalanceEditDTO
*/ */
public async changeOpeningBalance( public async changeOpeningBalance(
customerId: number, customerId: number,
openingBalanceEditDTO: ICustomerOpeningBalanceEditDTO, openingBalanceEditDTO: CustomerOpeningBalanceEditDto,
): Promise<Customer> { ): Promise<Customer> {
// Retrieves the old customer or throw not found error. // Retrieves the old customer or throw not found error.
const oldCustomer = await this.customerModel() const oldCustomer = await this.customerModel()

View File

@@ -4,6 +4,7 @@ import {
IsNotEmpty, IsNotEmpty,
IsNumber, IsNumber,
IsString, IsString,
ValidateIf,
} from 'class-validator'; } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
import { IsOptional, ToNumber } from '@/common/decorators/Validators'; import { IsOptional, ToNumber } from '@/common/decorators/Validators';
@@ -40,10 +41,11 @@ export class CreateCustomerDto extends ContactAddressDto {
@ApiProperty({ @ApiProperty({
required: false, required: false,
description: 'Opening balance date', description: 'Opening balance date (required when openingBalance is provided)',
example: '2024-01-01', example: '2024-01-01',
}) })
@IsOptional() @ValidateIf((o) => o.openingBalance != null)
@IsNotEmpty({ message: 'openingBalanceAt is required when openingBalance is provided' })
@IsString() @IsString()
openingBalanceAt?: string; openingBalanceAt?: string;

View File

@@ -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;
}

View File

@@ -1,91 +1,63 @@
// import { Service, Inject } from 'typedi'; import { Injectable } from '@nestjs/common';
// import { import { OnEvent } from '@nestjs/event-emitter';
// ICustomerEventCreatedPayload, import {
// ICustomerEventDeletedPayload, ICustomerEventCreatedPayload,
// ICustomerOpeningBalanceEditedPayload, ICustomerEventDeletedPayload,
// } from '@/interfaces'; ICustomerOpeningBalanceEditedPayload,
// import events from '@/subscribers/events'; } from '../types/Customers.types';
// import { CustomerGLEntriesStorage } from '../CustomerGLEntriesStorage'; import { events } from '@/common/events/events';
import { CustomerGLEntriesStorage } from '../CustomerGLEntriesStorage';
// @Service() @Injectable()
// export class CustomerWriteGLOpeningBalanceSubscriber { export class CustomerWriteGLOpeningBalanceSubscriber {
// @Inject() constructor(private readonly customerGLEntries: CustomerGLEntriesStorage) { }
// private customerGLEntries: CustomerGLEntriesStorage;
// /** /**
// * Attaches events with handlers. * Handles the writing opening balance journal entries once the customer created.
// */ */
// public attach(bus) { @OnEvent(events.customers.onCreated)
// bus.subscribe( public async handleWriteOpenBalanceEntries({
// events.customers.onCreated, customer,
// this.handleWriteOpenBalanceEntries trx,
// ); }: ICustomerEventCreatedPayload) {
// bus.subscribe( // Writes the customer opening balance journal entries.
// events.customers.onDeleted, if (customer.openingBalance) {
// this.handleRevertOpeningBalanceEntries await this.customerGLEntries.writeCustomerOpeningBalance(
// ); customer.id,
// bus.subscribe( trx,
// events.customers.onOpeningBalanceChanged, );
// this.handleRewriteOpeningEntriesOnChanged }
// ); }
// }
// /** /**
// * Handles the writing opening balance journal entries once the customer created. * Handles the deleting opening balance journal entries once the customer deleted.
// * @param {ICustomerEventCreatedPayload} payload - */
// */ @OnEvent(events.customers.onDeleted)
// private handleWriteOpenBalanceEntries = async ({ public async handleRevertOpeningBalanceEntries({
// tenantId, customerId,
// customer, trx,
// trx, }: ICustomerEventDeletedPayload) {
// }: ICustomerEventCreatedPayload) => { await this.customerGLEntries.revertCustomerOpeningBalance(customerId, trx);
// // Writes the customer opening balance journal entries. }
// if (customer.openingBalance) {
// await this.customerGLEntries.writeCustomerOpeningBalance(
// tenantId,
// customer.id,
// trx
// );
// }
// };
// /** /**
// * Handles the deleting opeing balance journal entrise once the customer deleted. * Handles the rewrite opening balance entries once opening balance changed.
// * @param {ICustomerEventDeletedPayload} payload - */
// */ @OnEvent(events.customers.onOpeningBalanceChanged)
// private handleRevertOpeningBalanceEntries = async ({ public async handleRewriteOpeningEntriesOnChanged({
// tenantId, customer,
// customerId, trx,
// trx, }: ICustomerOpeningBalanceEditedPayload) {
// }: ICustomerEventDeletedPayload) => { if (customer.openingBalance) {
// await this.customerGLEntries.revertCustomerOpeningBalance( await this.customerGLEntries.rewriteCustomerOpeningBalance(
// tenantId, customer.id,
// customerId, trx,
// trx );
// ); } else {
// }; await this.customerGLEntries.revertCustomerOpeningBalance(
customer.id,
// /** 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
// );
// }
// };
// }

View File

@@ -4,6 +4,7 @@ import { IContactAddressDTO } from '@/modules/Contacts/types/Contacts.types';
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types'; import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model'; import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { CreateCustomerDto } from '../dtos/CreateCustomer.dto'; import { CreateCustomerDto } from '../dtos/CreateCustomer.dto';
import { CustomerOpeningBalanceEditDto } from '../dtos/CustomerOpeningBalanceEdit.dto';
import { EditCustomerDto } from '../dtos/EditCustomer.dto'; import { EditCustomerDto } from '../dtos/EditCustomer.dto';
// Customer Interfaces. // Customer Interfaces.
@@ -113,23 +114,16 @@ export enum VendorAction {
View = 'View', View = 'View',
} }
export interface ICustomerOpeningBalanceEditDTO {
openingBalance: number;
openingBalanceAt: Date | string;
openingBalanceExchangeRate: number;
openingBalanceBranchId?: number;
}
export interface ICustomerOpeningBalanceEditingPayload { export interface ICustomerOpeningBalanceEditingPayload {
oldCustomer: Customer; oldCustomer: Customer;
openingBalanceEditDTO: ICustomerOpeningBalanceEditDTO; openingBalanceEditDTO: CustomerOpeningBalanceEditDto;
trx?: Knex.Transaction; trx?: Knex.Transaction;
} }
export interface ICustomerOpeningBalanceEditedPayload { export interface ICustomerOpeningBalanceEditedPayload {
customer: Customer; customer: Customer;
oldCustomer: Customer; oldCustomer: Customer;
openingBalanceEditDTO: ICustomerOpeningBalanceEditDTO; openingBalanceEditDTO: CustomerOpeningBalanceEditDto;
trx: Knex.Transaction; trx: Knex.Transaction;
} }

View File

@@ -1,115 +1,116 @@
// import { Service } from 'typedi'; import { Injectable } from '@nestjs/common';
// import { IVendor, AccountNormal, ILedgerEntry } from '@/interfaces'; import { AccountNormal } from '@/interfaces/Account';
// import Ledger from '@/services/Accounting/Ledger'; import { ILedgerEntry } from '@/modules/Ledger/types/Ledger.types';
import { Ledger } from '@/modules/Ledger/Ledger';
import { Vendor } from './models/Vendor';
// @Service() @Injectable()
// export class VendorGLEntries { export class VendorGLEntries {
// /** /**
// * Retrieves the opening balance GL common entry. * Retrieves the opening balance GL common entry.
// * @param {IVendor} vendor - * @param {Vendor} vendor -
// */ */
// private getOpeningBalanceGLCommonEntry = (vendor: IVendor) => { private getOpeningBalanceGLCommonEntry = (vendor: Vendor) => {
// return { return {
// exchangeRate: vendor.openingBalanceExchangeRate, exchangeRate: vendor.openingBalanceExchangeRate,
// currencyCode: vendor.currencyCode, currencyCode: vendor.currencyCode,
// transactionType: 'VendorOpeningBalance', transactionType: 'VendorOpeningBalance',
// transactionId: vendor.id, transactionId: vendor.id,
// date: vendor.openingBalanceAt, date: vendor.openingBalanceAt,
// userId: vendor.userId, contactId: vendor.id,
// contactId: vendor.id,
// credit: 0, credit: 0,
// debit: 0, debit: 0,
// branchId: vendor.openingBalanceBranchId, branchId: vendor.openingBalanceBranchId,
// }; };
// }; };
// /** /**
// * Retrieves the opening balance GL debit entry. * Retrieves the opening balance GL debit entry.
// * @param {number} costAccountId - * @param {number} costAccountId -
// * @param {IVendor} vendor * @param {Vendor} vendor
// * @returns {ILedgerEntry} * @returns {ILedgerEntry}
// */ */
// private getOpeningBalanceGLDebitEntry = ( private getOpeningBalanceGLDebitEntry = (
// costAccountId: number, costAccountId: number,
// vendor: IVendor vendor: Vendor
// ): ILedgerEntry => { ): ILedgerEntry => {
// const commonEntry = this.getOpeningBalanceGLCommonEntry(vendor); const commonEntry = this.getOpeningBalanceGLCommonEntry(vendor);
// return { return {
// ...commonEntry, ...commonEntry,
// accountId: costAccountId, accountId: costAccountId,
// accountNormal: AccountNormal.DEBIT, accountNormal: AccountNormal.DEBIT,
// debit: vendor.localOpeningBalance, debit: vendor.localOpeningBalance,
// credit: 0, credit: 0,
// index: 2, index: 2,
// }; };
// }; };
// /** /**
// * Retrieves the opening balance GL credit entry. * Retrieves the opening balance GL credit entry.
// * @param {number} APAccountId * @param {number} APAccountId
// * @param {IVendor} vendor * @param {Vendor} vendor
// * @returns {ILedgerEntry} * @returns {ILedgerEntry}
// */ */
// private getOpeningBalanceGLCreditEntry = ( private getOpeningBalanceGLCreditEntry = (
// APAccountId: number, APAccountId: number,
// vendor: IVendor vendor: Vendor
// ): ILedgerEntry => { ): ILedgerEntry => {
// const commonEntry = this.getOpeningBalanceGLCommonEntry(vendor); const commonEntry = this.getOpeningBalanceGLCommonEntry(vendor);
// return { return {
// ...commonEntry, ...commonEntry,
// accountId: APAccountId, accountId: APAccountId,
// accountNormal: AccountNormal.CREDIT, accountNormal: AccountNormal.CREDIT,
// credit: vendor.localOpeningBalance, credit: vendor.localOpeningBalance,
// index: 1, index: 1,
// }; };
// }; };
// /** /**
// * Retrieves the opening balance GL entries. * Retrieves the opening balance GL entries.
// * @param {number} APAccountId * @param {number} APAccountId
// * @param {number} costAccountId - * @param {number} costAccountId -
// * @param {IVendor} vendor * @param {Vendor} vendor
// * @returns {ILedgerEntry[]} * @returns {ILedgerEntry[]}
// */ */
// public getOpeningBalanceGLEntries = ( public getOpeningBalanceGLEntries = (
// APAccountId: number, APAccountId: number,
// costAccountId: number, costAccountId: number,
// vendor: IVendor vendor: Vendor
// ): ILedgerEntry[] => { ): ILedgerEntry[] => {
// const debitEntry = this.getOpeningBalanceGLDebitEntry( const debitEntry = this.getOpeningBalanceGLDebitEntry(
// costAccountId, costAccountId,
// vendor vendor
// ); );
// const creditEntry = this.getOpeningBalanceGLCreditEntry( const creditEntry = this.getOpeningBalanceGLCreditEntry(
// APAccountId, APAccountId,
// vendor vendor
// ); );
// return [debitEntry, creditEntry]; return [debitEntry, creditEntry];
// }; };
// /** /**
// * Retrieves the opening balance ledger. * Retrieves the opening balance ledger.
// * @param {number} APAccountId * @param {number} APAccountId
// * @param {number} costAccountId - * @param {number} costAccountId -
// * @param {IVendor} vendor * @param {Vendor} vendor
// * @returns {Ledger} * @returns {Ledger}
// */ */
// public getOpeningBalanceLedger = ( public getOpeningBalanceLedger = (
// APAccountId: number, APAccountId: number,
// costAccountId: number, costAccountId: number,
// vendor: IVendor vendor: Vendor
// ) => { ) => {
// const entries = this.getOpeningBalanceGLEntries( const entries = this.getOpeningBalanceGLEntries(
// APAccountId, APAccountId,
// costAccountId, costAccountId,
// vendor vendor
// ); );
// return new Ledger(entries); return new Ledger(entries);
// }; };
// } }

View File

@@ -1,88 +1,86 @@
// import { Knex } from 'knex'; import { Knex } from 'knex';
// import { Service, Inject } from 'typedi'; import { Inject, Injectable } from '@nestjs/common';
// import LedgerStorageService from '@/services/Accounting/LedgerStorageService'; import { LedgerStorageService } from '@/modules/Ledger/LedgerStorage.service';
// import HasTenancyService from '@/services/Tenancy/TenancyService'; import { AccountRepository } from '@/modules/Accounts/repositories/Account.repository';
// import { VendorGLEntries } from './VendorGLEntries'; import { VendorGLEntries } from './VendorGLEntries';
import { Vendor } from './models/Vendor';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
// @Service() @Injectable()
// export class VendorGLEntriesStorage { export class VendorGLEntriesStorage {
// @Inject() constructor(
// private tenancy: HasTenancyService; private readonly ledgerStorage: LedgerStorageService,
private readonly accountRepository: AccountRepository,
private readonly vendorGLEntries: VendorGLEntries,
// @Inject() @Inject(Vendor.name)
// private ledegrRepository: LedgerStorageService; private readonly vendorModel: TenantModelProxy<typeof Vendor>,
) { }
// @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);
// /** // Finds the expense account.
// * Vendor opening balance journals. const expenseAccount = await this.accountRepository.findOrCreateOtherExpensesAccount(
// * @param {number} tenantId {},
// * @param {number} vendorId trx,
// * @param {Knex.Transaction} trx );
// */ // Find or create the A/P account.
// public writeVendorOpeningBalance = async ( const APAccount =
// tenantId: number, await this.accountRepository.findOrCreateAccountsPayable(
// vendorId: number, vendor.currencyCode,
// trx?: Knex.Transaction {},
// ) => { trx,
// const { Vendor } = this.tenancy.models(tenantId); );
// const { accountRepository } = this.tenancy.repositories(tenantId); // 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({ * Writes the vendor opening balance GL entries.
// slug: 'other-expenses', * @param {number} vendorId
// }); * @param {Knex.Transaction} trx
// // Find or create the A/P account. */
// const APAccount = await accountRepository.findOrCreateAccountsPayable( public rewriteVendorOpeningBalance = async (
// vendor.currencyCode, vendorId: number,
// {}, trx?: Knex.Transaction,
// trx ) => {
// ); // Reverts the vendor opening balance entries first.
// // Retrieves the vendor opening balance ledger. await this.revertVendorOpeningBalance(vendorId, trx);
// const ledger = this.vendorGLEntries.getOpeningBalanceLedger(
// APAccount.id,
// expenseAccount.id,
// vendor
// );
// // Commits the ledger entries to the storage.
// await this.ledegrRepository.commit(tenantId, ledger, trx);
// };
// /** // Write the vendor opening balance entries.
// * Reverts the vendor opening balance GL entries. await this.writeVendorOpeningBalance(vendorId, trx);
// * @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);
// };
// }

View File

@@ -9,10 +9,7 @@ import {
Query, Query,
} from '@nestjs/common'; } from '@nestjs/common';
import { VendorsApplication } from './VendorsApplication.service'; import { VendorsApplication } from './VendorsApplication.service';
import { import { VendorOpeningBalanceEditDto } from './dtos/VendorOpeningBalanceEdit.dto';
IVendorOpeningBalanceEditDTO,
IVendorsFilter,
} from './types/Vendors.types';
import { import {
ApiOperation, ApiOperation,
ApiResponse, ApiResponse,
@@ -68,7 +65,7 @@ export class VendorsController {
@ApiOperation({ summary: 'Edit the given vendor opening balance.' }) @ApiOperation({ summary: 'Edit the given vendor opening balance.' })
editOpeningBalance( editOpeningBalance(
@Param('id') vendorId: number, @Param('id') vendorId: number,
@Body() openingBalanceDTO: IVendorOpeningBalanceEditDTO, @Body() openingBalanceDTO: VendorOpeningBalanceEditDto,
) { ) {
return this.vendorsApplication.editOpeningBalance( return this.vendorsApplication.editOpeningBalance(
vendorId, vendorId,

View File

@@ -18,9 +18,14 @@ import { VendorsExportable } from './VendorsExportable';
import { VendorsImportable } from './VendorsImportable'; import { VendorsImportable } from './VendorsImportable';
import { BulkDeleteVendorsService } from './BulkDeleteVendors.service'; import { BulkDeleteVendorsService } from './BulkDeleteVendors.service';
import { ValidateBulkDeleteVendorsService } from './ValidateBulkDeleteVendors.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({ @Module({
imports: [TenancyDatabaseModule, DynamicListModule], imports: [TenancyDatabaseModule, DynamicListModule, LedgerModule, AccountsModule],
controllers: [VendorsController], controllers: [VendorsController],
providers: [ providers: [
ActivateVendorService, ActivateVendorService,
@@ -38,7 +43,10 @@ import { ValidateBulkDeleteVendorsService } from './ValidateBulkDeleteVendors.se
TransformerInjectable, TransformerInjectable,
TenancyContext, TenancyContext,
VendorsExportable, VendorsExportable,
VendorsImportable VendorsImportable,
VendorGLEntries,
VendorGLEntriesStorage,
VendorsWriteGLOpeningSubscriber,
], ],
}) })
export class VendorsModule {} export class VendorsModule { }

View File

@@ -5,10 +5,7 @@ import { EditVendorService } from './commands/EditVendor.service';
import { DeleteVendorService } from './commands/DeleteVendor.service'; import { DeleteVendorService } from './commands/DeleteVendor.service';
import { EditOpeningBalanceVendorService } from './commands/EditOpeningBalanceVendor.service'; import { EditOpeningBalanceVendorService } from './commands/EditOpeningBalanceVendor.service';
import { GetVendorService } from './queries/GetVendor'; import { GetVendorService } from './queries/GetVendor';
import { import { VendorOpeningBalanceEditDto } from './dtos/VendorOpeningBalanceEdit.dto';
IVendorOpeningBalanceEditDTO,
IVendorsFilter,
} from './types/Vendors.types';
import { GetVendorsService } from './queries/GetVendors.service'; import { GetVendorsService } from './queries/GetVendors.service';
import { CreateVendorDto } from './dtos/CreateVendor.dto'; import { CreateVendorDto } from './dtos/CreateVendor.dto';
import { EditVendorDto } from './dtos/EditVendor.dto'; import { EditVendorDto } from './dtos/EditVendor.dto';
@@ -58,14 +55,14 @@ export class VendorsApplication {
} }
/** /**
* Changes the opening balance of the given customer. * Changes the opening balance of the given vendor.
* @param {number} vendorId * @param {number} vendorId
* @param {IVendorOpeningBalanceEditDTO} openingBalanceEditDTO * @param {VendorOpeningBalanceEditDto} openingBalanceEditDTO
* @returns {Promise<IVendor>} * @returns {Promise<IVendor>}
*/ */
public editOpeningBalance( public editOpeningBalance(
vendorId: number, vendorId: number,
openingBalanceEditDTO: IVendorOpeningBalanceEditDTO, openingBalanceEditDTO: VendorOpeningBalanceEditDto,
) { ) {
return this.editOpeningBalanceService.editOpeningBalance( return this.editOpeningBalanceService.editOpeningBalance(
vendorId, vendorId,
@@ -95,10 +92,7 @@ export class VendorsApplication {
vendorIds: number[], vendorIds: number[],
options?: { skipUndeletable?: boolean }, options?: { skipUndeletable?: boolean },
) { ) {
return this.bulkDeleteVendorsService.bulkDeleteVendors( return this.bulkDeleteVendorsService.bulkDeleteVendors(vendorIds, options);
vendorIds,
options,
);
} }
public validateBulkDeleteVendors(vendorIds: number[]) { public validateBulkDeleteVendors(vendorIds: number[]) {

View File

@@ -2,10 +2,10 @@ import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { import {
IVendorOpeningBalanceEditDTO,
IVendorOpeningBalanceEditedPayload, IVendorOpeningBalanceEditedPayload,
IVendorOpeningBalanceEditingPayload, IVendorOpeningBalanceEditingPayload,
} from '../types/Vendors.types'; } from '../types/Vendors.types';
import { VendorOpeningBalanceEditDto } from '../dtos/VendorOpeningBalanceEdit.dto';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service'; import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { Vendor } from '../models/Vendor'; import { Vendor } from '../models/Vendor';
import { events } from '@/common/events/events'; import { events } from '@/common/events/events';
@@ -29,12 +29,12 @@ export class EditOpeningBalanceVendorService {
/** /**
* Changes the opening balance of the given customer. * Changes the opening balance of the given customer.
* @param {number} vendorId * @param {number} vendorId
* @param {IVendorOpeningBalanceEditDTO} openingBalanceEditDTO * @param {VendorOpeningBalanceEditDto} openingBalanceEditDTO
* @returns {Promise<IVendor>} * @returns {Promise<IVendor>}
*/ */
public async editOpeningBalance( public async editOpeningBalance(
vendorId: number, vendorId: number,
openingBalanceEditDTO: IVendorOpeningBalanceEditDTO, openingBalanceEditDTO: VendorOpeningBalanceEditDto,
) { ) {
// Retrieves the old vendor or throw not found error. // Retrieves the old vendor or throw not found error.
const oldVendor = await this.vendorModel() const oldVendor = await this.vendorModel()

View File

@@ -2,11 +2,13 @@ import { ApiProperty } from '@nestjs/swagger';
import { import {
IsISO8601, IsISO8601,
IsInt, IsInt,
IsNotEmpty,
IsNumber, IsNumber,
Min, Min,
IsBoolean, IsBoolean,
IsEmail, IsEmail,
IsString, IsString,
ValidateIf,
} from 'class-validator'; } from 'class-validator';
import { ContactAddressDto } from '@/modules/Customers/dtos/ContactAddress.dto'; import { ContactAddressDto } from '@/modules/Customers/dtos/ContactAddress.dto';
import { IsOptional, ToNumber } from '@/common/decorators/Validators'; import { IsOptional, ToNumber } from '@/common/decorators/Validators';
@@ -30,8 +32,12 @@ export class CreateVendorDto extends ContactAddressDto {
@ToNumber() @ToNumber()
openingBalanceExchangeRate?: number; openingBalanceExchangeRate?: number;
@ApiProperty({ required: false, description: 'Date of the opening balance' }) @ApiProperty({
@IsOptional() 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() @IsISO8601()
openingBalanceAt?: Date; openingBalanceAt?: Date;

View File

@@ -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;
}

View File

@@ -36,6 +36,7 @@ export class Vendor extends TenantBaseModel {
openingBalance: number; openingBalance: number;
openingBalanceExchangeRate: number; openingBalanceExchangeRate: number;
openingBalanceAt: Date | string; openingBalanceAt: Date | string;
openingBalanceBranchId?: number;
salutation: string; salutation: string;
firstName: string; firstName: string;

View File

@@ -1,91 +1,71 @@
// import { Inject, Service } from 'typedi'; import { Injectable } from '@nestjs/common';
// import events from '@/subscribers/events'; import { OnEvent } from '@nestjs/event-emitter';
// import { VendorGLEntriesStorage } from '../VendorGLEntriesStorage'; import { events } from '@/common/events/events';
// import { import { VendorGLEntriesStorage } from '../VendorGLEntriesStorage';
// IVendorEventCreatedPayload, import {
// IVendorEventDeletedPayload, IVendorEventCreatedPayload,
// IVendorOpeningBalanceEditedPayload, IVendorEventDeletedPayload,
// } from '@/interfaces'; IVendorOpeningBalanceEditedPayload,
} from '../types/Vendors.types';
// @Service() @Injectable()
// export class VendorsWriteGLOpeningSubscriber { export class VendorsWriteGLOpeningSubscriber {
// @Inject() constructor(
// private vendorGLEntriesStorage: VendorGLEntriesStorage; private readonly vendorGLEntriesStorage: VendorGLEntriesStorage,
) {}
// /** /**
// * Constructor method. * Writes the open balance journal entries once the vendor created.
// */ * @param {IVendorEventCreatedPayload} payload -
// public attach(bus) { */
// bus.subscribe( @OnEvent(events.vendors.onCreated)
// events.vendors.onCreated, public async handleWriteOpeningBalanceEntries({
// this.handleWriteOpeningBalanceEntries vendor,
// ); trx,
// bus.subscribe( }: IVendorEventCreatedPayload) {
// events.vendors.onDeleted, // Writes the vendor opening balance journal entries.
// this.handleRevertOpeningBalanceEntries if (vendor.openingBalance) {
// ); await this.vendorGLEntriesStorage.writeVendorOpeningBalance(
// bus.subscribe( vendor.id,
// events.vendors.onOpeningBalanceChanged, trx,
// this.handleRewriteOpeningEntriesOnChanged );
// ); }
// } }
// /** /**
// * Writes the open balance journal entries once the vendor created. * Revert the opening balance journal entries once the vendor deleted.
// * @param {IVendorEventCreatedPayload} payload - * @param {IVendorEventDeletedPayload} payload -
// */ */
// private handleWriteOpeningBalanceEntries = async ({ @OnEvent(events.vendors.onDeleted)
// tenantId, public async handleRevertOpeningBalanceEntries({
// vendor, vendorId,
// trx, trx,
// }: IVendorEventCreatedPayload) => { }: IVendorEventDeletedPayload) {
// // Writes the vendor opening balance journal entries. await this.vendorGLEntriesStorage.revertVendorOpeningBalance(
// if (vendor.openingBalance) { vendorId,
// await this.vendorGLEntriesStorage.writeVendorOpeningBalance( trx,
// tenantId, );
// vendor.id, }
// trx
// );
// }
// };
// /** /**
// * Revert the opening balance journal entries once the vendor deleted. * Handles the rewrite opening balance entries once opening balance changed.
// * @param {IVendorEventDeletedPayload} payload - * @param {IVendorOpeningBalanceEditedPayload} payload -
// */ */
// private handleRevertOpeningBalanceEntries = async ({ @OnEvent(events.vendors.onOpeningBalanceChanged)
// tenantId, public async handleRewriteOpeningEntriesOnChanged({
// vendorId, vendor,
// trx, trx,
// }: IVendorEventDeletedPayload) => { }: IVendorOpeningBalanceEditedPayload) {
// await this.vendorGLEntriesStorage.revertVendorOpeningBalance( if (vendor.openingBalance) {
// tenantId, await this.vendorGLEntriesStorage.rewriteVendorOpeningBalance(
// vendorId, vendor.id,
// trx trx,
// ); );
// }; } else {
await this.vendorGLEntriesStorage.revertVendorOpeningBalance(
// /** vendor.id,
// * Handles the rewrite opening balance entries once opening balnace changed. trx,
// * @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
// );
// }
// };
// }

View File

@@ -7,6 +7,7 @@ import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/Dynam
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model'; import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { CreateVendorDto } from '../dtos/CreateVendor.dto'; import { CreateVendorDto } from '../dtos/CreateVendor.dto';
import { EditVendorDto } from '../dtos/EditVendor.dto'; import { EditVendorDto } from '../dtos/EditVendor.dto';
import { VendorOpeningBalanceEditDto } from '../dtos/VendorOpeningBalanceEdit.dto';
// ---------------------------------- // ----------------------------------
export interface IVendorNewDTO extends IContactAddressDTO { export interface IVendorNewDTO extends IContactAddressDTO {
@@ -92,23 +93,16 @@ export interface IVendorEventEditedPayload {
trx?: Knex.Transaction; trx?: Knex.Transaction;
} }
export interface IVendorOpeningBalanceEditDTO {
openingBalance: number;
openingBalanceAt: Date | string;
openingBalanceExchangeRate: number;
openingBalanceBranchId?: number;
}
export interface IVendorOpeningBalanceEditingPayload { export interface IVendorOpeningBalanceEditingPayload {
oldVendor: Vendor; oldVendor: Vendor;
openingBalanceEditDTO: IVendorOpeningBalanceEditDTO; openingBalanceEditDTO: VendorOpeningBalanceEditDto;
trx?: Knex.Transaction; trx?: Knex.Transaction;
} }
export interface IVendorOpeningBalanceEditedPayload { export interface IVendorOpeningBalanceEditedPayload {
vendor: Vendor; vendor: Vendor;
oldVendor: Vendor; oldVendor: Vendor;
openingBalanceEditDTO: IVendorOpeningBalanceEditDTO; openingBalanceEditDTO: VendorOpeningBalanceEditDto;
trx: Knex.Transaction; trx: Knex.Transaction;
} }