refactor: GL entries

This commit is contained in:
Ahmed Bouhuolia
2024-12-31 14:57:24 +02:00
parent 1b15261adb
commit a819d6c1ba
54 changed files with 2669 additions and 2298 deletions

View File

@@ -0,0 +1,181 @@
import { ILedgerEntry } from '@/modules/Ledger/types/Ledger.types';
import { PaymentReceived } from '../models/PaymentReceived';
import { sumBy } from 'lodash';
import { AccountNormal } from '@/interfaces/Account';
import { Ledger } from '@/modules/Ledger/Ledger';
export class PaymentReceivedGL {
private readonly paymentReceived: PaymentReceived;
private ARAccountId: number;
private exchangeGainOrLossAccountId: number;
private baseCurrencyCode: string;
/**
* Constructor method.
* @param {PaymentReceived} paymentReceived - Payment received.
*/
constructor(paymentReceived: PaymentReceived) {
this.paymentReceived = paymentReceived;
}
setARAccountId(ARAccountId: number) {
this.ARAccountId = ARAccountId;
return this;
}
setExchangeGainOrLossAccountId(exchangeGainOrLossAccountId: number) {
this.exchangeGainOrLossAccountId = exchangeGainOrLossAccountId;
return this;
}
setBaseCurrencyCode(baseCurrencyCode: string) {
this.baseCurrencyCode = baseCurrencyCode;
return this;
}
/**
* Calculates the payment total exchange gain/loss.
* @param {IBillPayment} paymentReceive - Payment receive with entries.
* @returns {number}
*/
private paymentExGainOrLoss = (): number => {
return sumBy(this.paymentReceived.entries, (entry) => {
const paymentLocalAmount =
entry.paymentAmount * this.paymentReceived.exchangeRate;
const invoicePayment = entry.paymentAmount * entry.invoice.exchangeRate;
return paymentLocalAmount - invoicePayment;
});
};
/**
* Retrieves the common entry of payment receive.
*/
private get paymentReceiveCommonEntry() {
return {
debit: 0,
credit: 0,
currencyCode: this.paymentReceived.currencyCode,
exchangeRate: this.paymentReceived.exchangeRate,
transactionId: this.paymentReceived.id,
transactionType: 'PaymentReceive',
transactionNumber: this.paymentReceived.paymentReceiveNo,
referenceNumber: this.paymentReceived.referenceNo,
date: this.paymentReceived.paymentDate,
userId: this.paymentReceived.userId,
createdAt: this.paymentReceived.createdAt,
branchId: this.paymentReceived.branchId,
};
}
/**
* Retrieves the payment exchange gain/loss entry.
* @param {IPaymentReceived} paymentReceive -
* @param {number} ARAccountId -
* @param {number} exchangeGainOrLossAccountId -
* @param {string} baseCurrencyCode -
* @returns {ILedgerEntry[]}
*/
private get paymentExchangeGainLossEntry(): ILedgerEntry[] {
const commonJournal = this.paymentReceiveCommonEntry;
const gainOrLoss = this.paymentExGainOrLoss();
const absGainOrLoss = Math.abs(gainOrLoss);
return gainOrLoss
? [
{
...commonJournal,
currencyCode: this.baseCurrencyCode,
exchangeRate: 1,
debit: gainOrLoss > 0 ? absGainOrLoss : 0,
credit: gainOrLoss < 0 ? absGainOrLoss : 0,
accountId: this.ARAccountId,
contactId: this.paymentReceived.customerId,
index: 3,
accountNormal: AccountNormal.CREDIT,
},
{
...commonJournal,
currencyCode: this.baseCurrencyCode,
exchangeRate: 1,
credit: gainOrLoss > 0 ? absGainOrLoss : 0,
debit: gainOrLoss < 0 ? absGainOrLoss : 0,
accountId: this.exchangeGainOrLossAccountId,
index: 3,
accountNormal: AccountNormal.DEBIT,
},
]
: [];
}
/**
* Retrieves the payment deposit GL entry.
* @param {IPaymentReceived} paymentReceive
* @returns {ILedgerEntry}
*/
private get paymentDepositGLEntry(): ILedgerEntry {
const commonJournal = this.paymentReceiveCommonEntry;
return {
...commonJournal,
debit: this.paymentReceived.localAmount,
accountId: this.paymentReceived.depositAccountId,
index: 2,
accountNormal: AccountNormal.DEBIT,
};
}
/**
* Retrieves the payment receivable entry.
* @param {IPaymentReceived} paymentReceive
* @param {number} ARAccountId
* @returns {ILedgerEntry}
*/
private get paymentReceivableEntry(): ILedgerEntry {
const commonJournal = this.paymentReceiveCommonEntry;
return {
...commonJournal,
credit: this.paymentReceived.localAmount,
contactId: this.paymentReceived.customerId,
accountId: this.ARAccountId,
index: 1,
accountNormal: AccountNormal.DEBIT,
};
}
/**
* Records payment receive journal transactions.
*
* Invoice payment journals.
* --------
* - Account receivable -> Debit
* - Payment account [current asset] -> Credit
* @returns {Promise<ILedgerEntry>}
*/
public GLEntries(): ILedgerEntry[] {
// Retrieve the payment deposit entry.
const paymentDepositEntry = this.paymentDepositGLEntry;
// Retrieves the A/R entry.
const receivableEntry = this.paymentReceivableEntry;
// Exchange gain/loss entries.
const gainLossEntries = this.paymentExchangeGainLossEntry;
return [paymentDepositEntry, receivableEntry, ...gainLossEntries];
}
/**
* Retrieves the payment receive ledger.
* @returns {Ledger}
*/
public getLedger = (): Ledger => {
return new Ledger(this.GLEntries());
};
}

View File

@@ -1,299 +1,110 @@
// import { Service, Inject } from 'typedi';
// import { sumBy } from 'lodash';
// import { Knex } from 'knex';
// import Ledger from '@/services/Accounting/Ledger';
// import TenancyService from '@/services/Tenancy/TenancyService';
// import {
// IPaymentReceived,
// ILedgerEntry,
// AccountNormal,
// IPaymentReceiveGLCommonEntry,
// } from '@/interfaces';
// import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
// import { TenantMetadata } from '@/system/models';
import { Knex } from 'knex';
import { PaymentReceivedGL } from './PaymentReceivedGL';
import { PaymentReceived } from '../models/PaymentReceived';
import { LedgerStorageService } from '@/modules/Ledger/LedgerStorage.service';
import { Ledger } from '@/modules/Ledger/Ledger';
import { AccountRepository } from '@/modules/Accounts/repositories/Account.repository';
import { Injectable } from '@nestjs/common';
import { Inject } from '@nestjs/common';
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
// @Service()
// export class PaymentReceivedGLEntries {
// @Inject()
// private tenancy: TenancyService;
@Injectable()
export class PaymentReceivedGLEntries {
constructor(
private readonly ledgerStorage: LedgerStorageService,
private readonly accountRepository: AccountRepository,
private readonly tenancyContext: TenancyContext,
// @Inject()
// private ledgerStorage: LedgerStorageService;
@Inject(PaymentReceived.name)
private readonly paymentReceivedModel: typeof PaymentReceived,
) {}
// /**
// * Writes payment GL entries to the storage.
// * @param {number} tenantId
// * @param {number} paymentReceiveId
// * @param {Knex.Transaction} trx
// * @returns {Promise<void>}
// */
// public writePaymentGLEntries = async (
// tenantId: number,
// paymentReceiveId: number,
// trx?: Knex.Transaction
// ): Promise<void> => {
// const { PaymentReceive } = this.tenancy.models(tenantId);
/**
* Writes payment GL entries to the storage.
* @param {number} paymentReceiveId - Payment received id.
* @param {Knex.Transaction} trx - Knex transaction.
* @returns {Promise<void>}
*/
public writePaymentGLEntries = async (
paymentReceiveId: number,
trx?: Knex.Transaction
): Promise<void> => {
// Retrieves the given tenant metadata.
const tenantMeta = await this.tenancyContext.getTenantMetadata();
// // Retrieves the given tenant metadata.
// const tenantMeta = await TenantMetadata.query().findOne({ tenantId });
// Retrieves the payment receive with associated entries.
const paymentReceive = await this.paymentReceivedModel
.query(trx)
.findById(paymentReceiveId)
.withGraphFetched('entries.invoice');
// // Retrieves the payment receive with associated entries.
// const paymentReceive = await PaymentReceive.query(trx)
// .findById(paymentReceiveId)
// .withGraphFetched('entries.invoice');
// Retrives the payment receive ledger.
const ledger = await this.getPaymentReceiveGLedger(
paymentReceive,
tenantMeta.baseCurrency,
);
// Commit the ledger entries to the storage.
await this.ledgerStorage.commit(ledger, trx);
};
// // Retrives the payment receive ledger.
// const ledger = await this.getPaymentReceiveGLedger(
// tenantId,
// paymentReceive,
// tenantMeta.baseCurrency,
// trx
// );
// // Commit the ledger entries to the storage.
// await this.ledgerStorage.commit(tenantId, ledger, trx);
// };
/**
* Reverts the given payment receive GL entries.
* @param {number} paymentReceiveId - Payment received id.
* @param {Knex.Transaction} trx - Knex transaction.
*/
public revertPaymentGLEntries = async (
paymentReceiveId: number,
trx?: Knex.Transaction
) => {
await this.ledgerStorage.deleteByReference(
paymentReceiveId,
'PaymentReceive',
trx
);
};
// /**
// * Reverts the given payment receive GL entries.
// * @param {number} tenantId
// * @param {number} paymentReceiveId
// * @param {Knex.Transaction} trx
// */
// public revertPaymentGLEntries = async (
// tenantId: number,
// paymentReceiveId: number,
// trx?: Knex.Transaction
// ) => {
// await this.ledgerStorage.deleteByReference(
// tenantId,
// paymentReceiveId,
// 'PaymentReceive',
// trx
// );
// };
/**
* Rewrites the given payment receive GL entries.
* @param {number} paymentReceiveId - Payment received id.
* @param {Knex.Transaction} trx - Knex transaction.
*/
public rewritePaymentGLEntries = async (
paymentReceiveId: number,
trx?: Knex.Transaction
) => {
// Reverts the payment GL entries.
await this.revertPaymentGLEntries(paymentReceiveId, trx);
// /**
// * Rewrites the given payment receive GL entries.
// * @param {number} tenantId
// * @param {number} paymentReceiveId
// * @param {Knex.Transaction} trx
// */
// public rewritePaymentGLEntries = async (
// tenantId: number,
// paymentReceiveId: number,
// trx?: Knex.Transaction
// ) => {
// // Reverts the payment GL entries.
// await this.revertPaymentGLEntries(tenantId, paymentReceiveId, trx);
// Writes the payment GL entries.
await this.writePaymentGLEntries(paymentReceiveId, trx);
};
// // Writes the payment GL entries.
// await this.writePaymentGLEntries(tenantId, paymentReceiveId, trx);
// };
/**
* Retrieves the payment receive general ledger.
* @param {IPaymentReceived} paymentReceive - Payment received.
* @param {string} baseCurrencyCode - Base currency code.
* @param {Knex.Transaction} trx - Knex transaction.
* @returns {Ledger}
*/
public getPaymentReceiveGLedger = async (
paymentReceive: PaymentReceived,
baseCurrencyCode: string,
): Promise<Ledger> => {
// Retrieve the A/R account of the given currency.
const receivableAccount =
await this.accountRepository.findOrCreateAccountReceivable(
paymentReceive.currencyCode
);
// Exchange gain/loss account.
const exGainLossAccount = await this.accountRepository.findBySlug(
'exchange-grain-loss'
);
const paymentReceivedGL = new PaymentReceivedGL(paymentReceive)
.setARAccountId(receivableAccount.id)
.setExchangeGainOrLossAccountId(exGainLossAccount.id)
.setBaseCurrencyCode(baseCurrencyCode);
// /**
// * Retrieves the payment receive general ledger.
// * @param {number} tenantId -
// * @param {IPaymentReceived} paymentReceive -
// * @param {string} baseCurrencyCode -
// * @param {Knex.Transaction} trx -
// * @returns {Ledger}
// */
// public getPaymentReceiveGLedger = async (
// tenantId: number,
// paymentReceive: IPaymentReceived,
// baseCurrencyCode: string,
// trx?: Knex.Transaction
// ): Promise<Ledger> => {
// const { Account } = this.tenancy.models(tenantId);
// const { accountRepository } = this.tenancy.repositories(tenantId);
return paymentReceivedGL.getLedger();
};
// // Retrieve the A/R account of the given currency.
// const receivableAccount =
// await accountRepository.findOrCreateAccountReceivable(
// paymentReceive.currencyCode
// );
// // Exchange gain/loss account.
// const exGainLossAccount = await Account.query(trx).modify(
// 'findBySlug',
// 'exchange-grain-loss'
// );
// const ledgerEntries = this.getPaymentReceiveGLEntries(
// paymentReceive,
// receivableAccount.id,
// exGainLossAccount.id,
// baseCurrencyCode
// );
// return new Ledger(ledgerEntries);
// };
// /**
// * Calculates the payment total exchange gain/loss.
// * @param {IBillPayment} paymentReceive - Payment receive with entries.
// * @returns {number}
// */
// private getPaymentExGainOrLoss = (
// paymentReceive: IPaymentReceived
// ): number => {
// return sumBy(paymentReceive.entries, (entry) => {
// const paymentLocalAmount =
// entry.paymentAmount * paymentReceive.exchangeRate;
// const invoicePayment = entry.paymentAmount * entry.invoice.exchangeRate;
// return paymentLocalAmount - invoicePayment;
// });
// };
// /**
// * Retrieves the common entry of payment receive.
// * @param {IPaymentReceived} paymentReceive
// * @returns {}
// */
// private getPaymentReceiveCommonEntry = (
// paymentReceive: IPaymentReceived
// ): IPaymentReceiveGLCommonEntry => {
// return {
// debit: 0,
// credit: 0,
// currencyCode: paymentReceive.currencyCode,
// exchangeRate: paymentReceive.exchangeRate,
// transactionId: paymentReceive.id,
// transactionType: 'PaymentReceive',
// transactionNumber: paymentReceive.paymentReceiveNo,
// referenceNumber: paymentReceive.referenceNo,
// date: paymentReceive.paymentDate,
// userId: paymentReceive.userId,
// createdAt: paymentReceive.createdAt,
// branchId: paymentReceive.branchId,
// };
// };
// /**
// * Retrieves the payment exchange gain/loss entry.
// * @param {IPaymentReceived} paymentReceive -
// * @param {number} ARAccountId -
// * @param {number} exchangeGainOrLossAccountId -
// * @param {string} baseCurrencyCode -
// * @returns {ILedgerEntry[]}
// */
// private getPaymentExchangeGainLossEntry = (
// paymentReceive: IPaymentReceived,
// ARAccountId: number,
// exchangeGainOrLossAccountId: number,
// baseCurrencyCode: string
// ): ILedgerEntry[] => {
// const commonJournal = this.getPaymentReceiveCommonEntry(paymentReceive);
// const gainOrLoss = this.getPaymentExGainOrLoss(paymentReceive);
// const absGainOrLoss = Math.abs(gainOrLoss);
// return gainOrLoss
// ? [
// {
// ...commonJournal,
// currencyCode: baseCurrencyCode,
// exchangeRate: 1,
// debit: gainOrLoss > 0 ? absGainOrLoss : 0,
// credit: gainOrLoss < 0 ? absGainOrLoss : 0,
// accountId: ARAccountId,
// contactId: paymentReceive.customerId,
// index: 3,
// accountNormal: AccountNormal.CREDIT,
// },
// {
// ...commonJournal,
// currencyCode: baseCurrencyCode,
// exchangeRate: 1,
// credit: gainOrLoss > 0 ? absGainOrLoss : 0,
// debit: gainOrLoss < 0 ? absGainOrLoss : 0,
// accountId: exchangeGainOrLossAccountId,
// index: 3,
// accountNormal: AccountNormal.DEBIT,
// },
// ]
// : [];
// };
// /**
// * Retrieves the payment deposit GL entry.
// * @param {IPaymentReceived} paymentReceive
// * @returns {ILedgerEntry}
// */
// private getPaymentDepositGLEntry = (
// paymentReceive: IPaymentReceived
// ): ILedgerEntry => {
// const commonJournal = this.getPaymentReceiveCommonEntry(paymentReceive);
// return {
// ...commonJournal,
// debit: paymentReceive.localAmount,
// accountId: paymentReceive.depositAccountId,
// index: 2,
// accountNormal: AccountNormal.DEBIT,
// };
// };
// /**
// * Retrieves the payment receivable entry.
// * @param {IPaymentReceived} paymentReceive
// * @param {number} ARAccountId
// * @returns {ILedgerEntry}
// */
// private getPaymentReceivableEntry = (
// paymentReceive: IPaymentReceived,
// ARAccountId: number
// ): ILedgerEntry => {
// const commonJournal = this.getPaymentReceiveCommonEntry(paymentReceive);
// return {
// ...commonJournal,
// credit: paymentReceive.localAmount,
// contactId: paymentReceive.customerId,
// accountId: ARAccountId,
// index: 1,
// accountNormal: AccountNormal.DEBIT,
// };
// };
// /**
// * Records payment receive journal transactions.
// *
// * Invoice payment journals.
// * --------
// * - Account receivable -> Debit
// * - Payment account [current asset] -> Credit
// *
// * @param {number} tenantId
// * @param {IPaymentReceived} paymentRecieve - Payment receive model.
// * @param {number} ARAccountId - A/R account id.
// * @param {number} exGainOrLossAccountId - Exchange gain/loss account id.
// * @param {string} baseCurrency - Base currency code.
// * @returns {Promise<ILedgerEntry>}
// */
// public getPaymentReceiveGLEntries = (
// paymentReceive: IPaymentReceived,
// ARAccountId: number,
// exGainOrLossAccountId: number,
// baseCurrency: string
// ): ILedgerEntry[] => {
// // Retrieve the payment deposit entry.
// const paymentDepositEntry = this.getPaymentDepositGLEntry(paymentReceive);
// // Retrieves the A/R entry.
// const receivableEntry = this.getPaymentReceivableEntry(
// paymentReceive,
// ARAccountId
// );
// // Exchange gain/loss entries.
// const gainLossEntries = this.getPaymentExchangeGainLossEntry(
// paymentReceive,
// ARAccountId,
// exGainOrLossAccountId,
// baseCurrency
// );
// return [paymentDepositEntry, receivableEntry, ...gainLossEntries];
// };
// }
}