refactor(nestjs): landed cost

This commit is contained in:
Ahmed Bouhuolia
2025-06-11 14:04:37 +02:00
parent 1130975efd
commit ff93168d72
28 changed files with 622 additions and 417 deletions

View File

@@ -23,7 +23,7 @@ export class AllocateLandedCostService extends BaseLandedCostService {
private readonly billModel: TenantModelProxy<typeof Bill>,
@Inject(BillLandedCost.name)
private readonly billLandedCostModel: TenantModelProxy<typeof BillLandedCost>
protected readonly billLandedCostModel: TenantModelProxy<typeof BillLandedCost>
) {
super();
}
@@ -54,7 +54,7 @@ export class AllocateLandedCostService extends BaseLandedCostService {
const amount = this.getAllocateItemsCostTotal(allocateCostDTO);
// Retrieve the purchase invoice or throw not found error.
const bill = await Bill.query()
const bill = await this.billModel().query()
.findById(billId)
.withGraphFetched('entries')
.throwIfNotFound();

View File

@@ -6,6 +6,8 @@ import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { Bill } from '@/modules/Bills/models/Bill';
import { BillLandedCost } from '../models/BillLandedCost';
import { IBillLandedCostTransaction } from '../types/BillLandedCosts.types';
import { ModelObject } from 'objection';
import { formatNumber } from '@/utils/format-number';
@Injectable()
export class BillAllocatedLandedCostTransactions {
@@ -23,19 +25,17 @@ export class BillAllocatedLandedCostTransactions {
/**
* Retrieve the bill associated landed cost transactions.
* @param {number} tenantId - Tenant id.
* @param {number} billId - Bill id.
* @return {Promise<IBillLandedCostTransaction>}
*/
public getBillLandedCostTransactions = async (
billId: number,
): Promise<IBillLandedCostTransaction> => {
): Promise<Array<IBillLandedCostTransaction>> => {
// Retrieve the given bill id or throw not found service error.
const bill = await this.billModel()
.query()
.findById(billId)
.throwIfNotFound();
// Retrieve the bill associated allocated landed cost with bill and expense entry.
const landedCostTransactions = await this.billLandedCostModel()
.query()
@@ -45,11 +45,8 @@ export class BillAllocatedLandedCostTransactions {
.withGraphFetched('allocatedFromExpenseEntry.expenseAccount')
.withGraphFetched('bill');
const transactionsJson = this.i18nService.i18nApply(
[[qim.$each, 'allocationMethodFormatted']],
landedCostTransactions.map((a) => a.toJSON()),
tenantId,
);
const transactionsJson = landedCostTransactions.map((a) => a.toJSON());
return this.transformBillLandedCostTransactions(transactionsJson);
};
@@ -59,7 +56,7 @@ export class BillAllocatedLandedCostTransactions {
* @returns
*/
private transformBillLandedCostTransactions = (
landedCostTransactions: IBillLandedCostTransaction[],
landedCostTransactions: ModelObject<BillLandedCost>[],
) => {
return landedCostTransactions.map(this.transformBillLandedCostTransaction);
};
@@ -70,15 +67,16 @@ export class BillAllocatedLandedCostTransactions {
* @returns
*/
private transformBillLandedCostTransaction = (
transaction: IBillLandedCostTransaction,
) => {
const getTransactionName = R.curry(this.condBillLandedTransactionName)(
transaction: ModelObject<BillLandedCost>,
): IBillLandedCostTransaction => {
const name = this.condBillLandedTransactionName(
transaction.fromTransactionType,
transaction,
);
const description = this.condBillLandedTransactionDescription(
transaction.fromTransactionType,
transaction,
);
const getTransactionDesc = R.curry(
this.condBillLandedTransactionDescription,
)(transaction.fromTransactionType);
return {
formattedAmount: formatNumber(transaction.amount, {
currencyCode: transaction.currencyCode,
@@ -87,8 +85,8 @@ export class BillAllocatedLandedCostTransactions {
'allocatedFromBillEntry',
'allocatedFromExpenseEntry',
]),
name: getTransactionName(transaction),
description: getTransactionDesc(transaction),
name,
description,
formattedLocalAmount: formatNumber(transaction.localAmount, {
currencyCode: 'USD',
}),

View File

@@ -0,0 +1,61 @@
import { isEmpty } from 'lodash';
import {
ILandedCostTransactionEntry,
ILandedCostTransaction,
} from '../types/BillLandedCosts.types';
import { Injectable } from '@nestjs/common';
import { Bill } from '@/modules/Bills/models/Bill';
import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry';
import { Item } from '@/modules/Items/models/Item';
import { ModelObject } from 'objection';
@Injectable()
export class BillLandedCost {
/**
* Retrieve the landed cost transaction from the given bill transaction.
* @param {IBill} bill - Bill transaction.
* @returns {ILandedCostTransaction} - Landed cost transaction.
*/
public transformToLandedCost = (
bill: ModelObject<Bill>,
): ILandedCostTransaction => {
const name = bill.billNumber || bill.referenceNo;
return {
id: bill.id,
name,
allocatedCostAmount: bill.allocatedCostAmount,
amount: bill.landedCostAmount,
unallocatedCostAmount: bill.unallocatedCostAmount,
transactionType: 'Bill',
currencyCode: bill.currencyCode,
exchangeRate: bill.exchangeRate,
...(!isEmpty(bill.entries) && {
entries: bill.entries.map(this.transformToLandedCostEntry),
}),
};
};
/**
* Transformes bill entry to landed cost entry.
* @param {IBill} bill - Bill model.
* @param {IItemEntry} billEntry - Bill entry.
* @return {ILandedCostTransactionEntry}
*/
public transformToLandedCostEntry(
billEntry: ItemEntry & { item: Item },
): ILandedCostTransactionEntry {
return {
id: billEntry.id,
name: billEntry.item.name,
code: billEntry.item.code,
amount: billEntry.amount,
unallocatedCostAmount: billEntry.unallocatedCostAmount,
allocatedCostAmount: billEntry.allocatedCostAmount,
description: billEntry.description,
costAccountId: billEntry.costAccountId || billEntry.item.costAccountId,
};
}
}

View File

@@ -0,0 +1,59 @@
import { Expense } from '@/modules/Expenses/models/Expense.model';
import { Injectable } from '@nestjs/common';
import { isEmpty } from 'lodash';
import { ModelObject } from 'objection';
import {
ILandedCostTransaction,
ILandedCostTransactionEntry,
} from '../types/BillLandedCosts.types';
import { ExpenseCategory } from '@/modules/Expenses/models/ExpenseCategory.model';
import { Account } from '@/modules/Accounts/models/Account.model';
@Injectable()
export class ExpenseLandedCost {
/**
* Retrieve the landed cost transaction from the given expense transaction.
* @param {IExpense} expense
* @returns {ILandedCostTransaction}
*/
public transformToLandedCost = (
expense: ModelObject<Expense>,
): ILandedCostTransaction => {
const name = 'EXP-100';
return {
id: expense.id,
name,
amount: expense.landedCostAmount,
allocatedCostAmount: expense.allocatedCostAmount,
unallocatedCostAmount: expense.unallocatedCostAmount,
transactionType: 'Expense',
currencyCode: expense.currencyCode,
exchangeRate: expense.exchangeRate || 1,
...(!isEmpty(expense.categories) && {
entries: expense.categories.map(this.transformToLandedCostEntry),
}),
};
};
/**
* Transformes expense entry to landed cost entry.
* @param {IExpenseCategory & { expenseAccount: IAccount }} expenseEntry -
* @return {ILandedCostTransactionEntry}
*/
public transformToLandedCostEntry = (
expenseEntry: ExpenseCategory & { expenseAccount: Account },
): ILandedCostTransactionEntry => {
return {
id: expenseEntry.id,
name: expenseEntry.expenseAccount.name,
code: expenseEntry.expenseAccount.code,
amount: expenseEntry.amount,
description: expenseEntry.description,
allocatedCostAmount: expenseEntry.allocatedCostAmount,
unallocatedCostAmount: expenseEntry.unallocatedCostAmount,
costAccountId: expenseEntry.expenseAccount.id,
};
};
}

View File

@@ -1,234 +1,236 @@
import * as R from 'ramda';
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { BaseLandedCostService } from '../BaseLandedCost.service';
import { BillLandedCost } from '../models/BillLandedCost';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { Bill } from '@/modules/Bills/models/Bill';
import { BillLandedCostEntry } from '../models/BillLandedCostEntry';
import { ILedger, ILedgerEntry } from '@/modules/Ledger/types/Ledger.types';
import { Ledger } from '@/modules/Ledger/Ledger';
// import * as R from 'ramda';
// import { Knex } from 'knex';
// import { Inject, Injectable } from '@nestjs/common';
// import { BaseLandedCostService } from '../BaseLandedCost.service';
// import { BillLandedCost } from '../models/BillLandedCost';
// import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
// import { Bill } from '@/modules/Bills/models/Bill';
// import { BillLandedCostEntry } from '../models/BillLandedCostEntry';
// import { ILedger, ILedgerEntry } from '@/modules/Ledger/types/Ledger.types';
// import { Ledger } from '@/modules/Ledger/Ledger';
// import { AccountNormal } from '@/interfaces/Account';
// import { ILandedCostTransactionEntry } from '../types/BillLandedCosts.types';
@Injectable()
export class LandedCostGLEntries extends BaseLandedCostService {
constructor(
private readonly journalService: JournalPosterService,
private readonly ledgerRepository: LedgerRepository,
// @Injectable()
// export class LandedCostGLEntries extends BaseLandedCostService {
// constructor(
// private readonly journalService: JournalPosterService,
// private readonly ledgerRepository: LedgerRepository,
@Inject(BillLandedCost.name)
private readonly billLandedCostModel: TenantModelProxy<typeof BillLandedCost>,
) {
super();
}
// @Inject(BillLandedCost.name)
// private readonly billLandedCostModel: TenantModelProxy<typeof BillLandedCost>,
// ) {
// super();
// }
/**
* Retrieves the landed cost GL common entry.
* @param {IBill} bill
* @param {IBillLandedCost} allocatedLandedCost
* @returns
*/
private getLandedCostGLCommonEntry = (
bill: Bill,
allocatedLandedCost: BillLandedCost
) => {
return {
date: bill.billDate,
currencyCode: allocatedLandedCost.currencyCode,
exchangeRate: allocatedLandedCost.exchangeRate,
// /**
// * Retrieves the landed cost GL common entry.
// * @param {IBill} bill
// * @param {IBillLandedCost} allocatedLandedCost
// * @returns
// */
// private getLandedCostGLCommonEntry = (
// bill: Bill,
// allocatedLandedCost: BillLandedCost
// ) => {
// return {
// date: bill.billDate,
// currencyCode: allocatedLandedCost.currencyCode,
// exchangeRate: allocatedLandedCost.exchangeRate,
transactionType: 'LandedCost',
transactionId: allocatedLandedCost.id,
transactionNumber: bill.billNumber,
// transactionType: 'LandedCost',
// transactionId: allocatedLandedCost.id,
// transactionNumber: bill.billNumber,
referenceNumber: bill.referenceNo,
// referenceNumber: bill.referenceNo,
credit: 0,
debit: 0,
};
};
// credit: 0,
// debit: 0,
// };
// };
/**
* Retrieves the landed cost GL inventory entry.
* @param {IBill} bill
* @param {IBillLandedCost} allocatedLandedCost
* @param {IBillLandedCostEntry} allocatedEntry
* @returns {ILedgerEntry}
*/
private getLandedCostGLInventoryEntry = (
bill: Bill,
allocatedLandedCost: BillLandedCost,
allocatedEntry: BillLandedCostEntry
): ILedgerEntry => {
const commonEntry = this.getLandedCostGLCommonEntry(
bill,
allocatedLandedCost
);
return {
...commonEntry,
debit: allocatedLandedCost.localAmount,
accountId: allocatedEntry.itemEntry.item.inventoryAccountId,
index: 1,
accountNormal: AccountNormal.DEBIT,
};
};
// /**
// * Retrieves the landed cost GL inventory entry.
// * @param {IBill} bill
// * @param {IBillLandedCost} allocatedLandedCost
// * @param {IBillLandedCostEntry} allocatedEntry
// * @returns {ILedgerEntry}
// */
// private getLandedCostGLInventoryEntry = (
// bill: Bill,
// allocatedLandedCost: BillLandedCost,
// allocatedEntry: BillLandedCostEntry
// ): ILedgerEntry => {
// const commonEntry = this.getLandedCostGLCommonEntry(
// bill,
// allocatedLandedCost
// );
// return {
// ...commonEntry,
// debit: allocatedLandedCost.localAmount,
// accountId: allocatedEntry.itemEntry.item.inventoryAccountId,
// index: 1,
// accountNormal: AccountNormal.DEBIT,
// };
// };
/**
* Retrieves the landed cost GL cost entry.
* @param {IBill} bill
* @param {IBillLandedCost} allocatedLandedCost
* @param {ILandedCostTransactionEntry} fromTransactionEntry
* @returns {ILedgerEntry}
*/
private getLandedCostGLCostEntry = (
bill: Bill,
allocatedLandedCost: BillLandedCost,
fromTransactionEntry: ILandedCostTransactionEntry
): ILedgerEntry => {
const commonEntry = this.getLandedCostGLCommonEntry(
bill,
allocatedLandedCost
);
return {
...commonEntry,
credit: allocatedLandedCost.localAmount,
accountId: fromTransactionEntry.costAccountId,
index: 2,
accountNormal: AccountNormal.CREDIT,
};
};
// /**
// * Retrieves the landed cost GL cost entry.
// * @param {IBill} bill
// * @param {IBillLandedCost} allocatedLandedCost
// * @param {ILandedCostTransactionEntry} fromTransactionEntry
// * @returns {ILedgerEntry}
// */
// private getLandedCostGLCostEntry = (
// bill: Bill,
// allocatedLandedCost: BillLandedCost,
// fromTransactionEntry: ILandedCostTransactionEntry
// ): ILedgerEntry => {
// const commonEntry = this.getLandedCostGLCommonEntry(
// bill,
// allocatedLandedCost
// );
// return {
// ...commonEntry,
// credit: allocatedLandedCost.localAmount,
// accountId: fromTransactionEntry.costAccountId,
// index: 2,
// accountNormal: AccountNormal.CREDIT,
// };
// };
/**
* Retrieve allocated landed cost entry GL entries.
* @param {IBill} bill
* @param {IBillLandedCost} allocatedLandedCost
* @param {ILandedCostTransactionEntry} fromTransactionEntry
* @param {IBillLandedCostEntry} allocatedEntry
* @returns {ILedgerEntry}
*/
private getLandedCostGLAllocateEntry = R.curry(
(
bill: Bill,
allocatedLandedCost: BillLandedCost,
fromTransactionEntry: LandedCostTransactionEntry,
allocatedEntry: BillLandedCostEntry
): ILedgerEntry[] => {
const inventoryEntry = this.getLandedCostGLInventoryEntry(
bill,
allocatedLandedCost,
allocatedEntry
);
const costEntry = this.getLandedCostGLCostEntry(
bill,
allocatedLandedCost,
fromTransactionEntry
);
return [inventoryEntry, costEntry];
}
);
// /**
// * Retrieve allocated landed cost entry GL entries.
// * @param {IBill} bill
// * @param {IBillLandedCost} allocatedLandedCost
// * @param {ILandedCostTransactionEntry} fromTransactionEntry
// * @param {IBillLandedCostEntry} allocatedEntry
// * @returns {ILedgerEntry}
// */
// private getLandedCostGLAllocateEntry = R.curry(
// (
// bill: Bill,
// allocatedLandedCost: BillLandedCost,
// fromTransactionEntry: ILandedCostTransactionEntry,
// allocatedEntry: BillLandedCostEntry
// ): ILedgerEntry[] => {
// const inventoryEntry = this.getLandedCostGLInventoryEntry(
// bill,
// allocatedLandedCost,
// allocatedEntry
// );
// const costEntry = this.getLandedCostGLCostEntry(
// bill,
// allocatedLandedCost,
// fromTransactionEntry
// );
// return [inventoryEntry, costEntry];
// }
// );
/**
* Compose the landed cost GL entries.
* @param {BillLandedCost} allocatedLandedCost
* @param {Bill} bill
* @param {ILandedCostTransactionEntry} fromTransactionEntry
* @returns {ILedgerEntry[]}
*/
public getLandedCostGLEntries = (
allocatedLandedCost: BillLandedCost,
bill: Bill,
fromTransactionEntry: LandedCostTransactionEntry
): ILedgerEntry[] => {
const getEntry = this.getLandedCostGLAllocateEntry(
bill,
allocatedLandedCost,
fromTransactionEntry
);
return allocatedLandedCost.allocateEntries.map(getEntry).flat();
};
// /**
// * Compose the landed cost GL entries.
// * @param {BillLandedCost} allocatedLandedCost
// * @param {Bill} bill
// * @param {ILandedCostTransactionEntry} fromTransactionEntry
// * @returns {ILedgerEntry[]}
// */
// public getLandedCostGLEntries = (
// allocatedLandedCost: BillLandedCost,
// bill: Bill,
// fromTransactionEntry: ILandedCostTransactionEntry
// ): ILedgerEntry[] => {
// const getEntry = this.getLandedCostGLAllocateEntry(
// bill,
// allocatedLandedCost,
// fromTransactionEntry
// );
// return allocatedLandedCost.allocateEntries.map(getEntry).flat();
// };
/**
* Retrieves the landed cost GL ledger.
* @param {IBillLandedCost} allocatedLandedCost
* @param {Bill} bill
* @param {ILandedCostTransactionEntry} fromTransactionEntry
* @returns {ILedger}
*/
public getLandedCostLedger = (
allocatedLandedCost: BillLandedCost,
bill: Bill,
fromTransactionEntry: LandedCostTransactionEntry
): ILedger => {
const entries = this.getLandedCostGLEntries(
allocatedLandedCost,
bill,
fromTransactionEntry
);
return new Ledger(entries);
};
// /**
// * Retrieves the landed cost GL ledger.
// * @param {BillLandedCost} allocatedLandedCost
// * @param {Bill} bill
// * @param {ILandedCostTransactionEntry} fromTransactionEntry
// * @returns {ILedger}
// */
// public getLandedCostLedger = (
// allocatedLandedCost: BillLandedCost,
// bill: Bill,
// fromTransactionEntry: ILandedCostTransactionEntry
// ): ILedger => {
// const entries = this.getLandedCostGLEntries(
// allocatedLandedCost,
// bill,
// fromTransactionEntry
// );
// return new Ledger(entries);
// };
/**
* Writes landed cost GL entries to the storage layer.
* @param {number} tenantId -
*/
public writeLandedCostGLEntries = async (
allocatedLandedCost: BillLandedCost,
bill: Bill,
fromTransactionEntry: ILandedCostTransactionEntry,
trx?: Knex.Transaction
) => {
const ledgerEntries = this.getLandedCostGLEntries(
allocatedLandedCost,
bill,
fromTransactionEntry
);
await this.ledgerRepository.saveLedgerEntries(ledgerEntries, trx);
};
// /**
// * Writes landed cost GL entries to the storage layer.
// * @param {number} tenantId -
// */
// public writeLandedCostGLEntries = async (
// allocatedLandedCost: BillLandedCost,
// bill: Bill,
// fromTransactionEntry: ILandedCostTransactionEntry,
// trx?: Knex.Transaction
// ) => {
// const ledgerEntries = this.getLandedCostGLEntries(
// allocatedLandedCost,
// bill,
// fromTransactionEntry
// );
// await this.ledgerRepository.saveLedgerEntries(ledgerEntries, trx);
// };
/**
* Generates and writes GL entries of the given landed cost.
* @param {number} billLandedCostId
* @param {Knex.Transaction} trx
*/
public createLandedCostGLEntries = async (
billLandedCostId: number,
trx?: Knex.Transaction
) => {
// Retrieve the bill landed cost transacion with associated
// allocated entries and items.
const allocatedLandedCost = await this.billLandedCostModel().query(trx)
.findById(billLandedCostId)
.withGraphFetched('bill')
.withGraphFetched('allocateEntries.itemEntry.item');
// /**
// * Generates and writes GL entries of the given landed cost.
// * @param {number} billLandedCostId
// * @param {Knex.Transaction} trx
// */
// public createLandedCostGLEntries = async (
// billLandedCostId: number,
// trx?: Knex.Transaction
// ) => {
// // Retrieve the bill landed cost transacion with associated
// // allocated entries and items.
// const allocatedLandedCost = await this.billLandedCostModel().query(trx)
// .findById(billLandedCostId)
// .withGraphFetched('bill')
// .withGraphFetched('allocateEntries.itemEntry.item');
// Retrieve the allocated from transactione entry.
const transactionEntry = await this.getLandedCostEntry(
allocatedLandedCost.fromTransactionType,
allocatedLandedCost.fromTransactionId,
allocatedLandedCost.fromTransactionEntryId
);
// Writes the given landed cost GL entries to the storage layer.
await this.writeLandedCostGLEntries(
allocatedLandedCost,
allocatedLandedCost.bill,
transactionEntry,
trx
);
};
// // Retrieve the allocated from transactione entry.
// const transactionEntry = await this.getLandedCostEntry(
// allocatedLandedCost.fromTransactionType,
// allocatedLandedCost.fromTransactionId,
// allocatedLandedCost.fromTransactionEntryId
// );
// // Writes the given landed cost GL entries to the storage layer.
// await this.writeLandedCostGLEntries(
// allocatedLandedCost,
// allocatedLandedCost.bill,
// transactionEntry,
// trx
// );
// };
/**
* Reverts GL entries of the given allocated landed cost transaction.
* @param {number} tenantId
* @param {number} landedCostId
* @param {Knex.Transaction} trx
*/
public revertLandedCostGLEntries = async (
landedCostId: number,
trx: Knex.Transaction
) => {
await this.journalService.revertJournalTransactions(
landedCostId,
'LandedCost',
trx
);
};
}
// /**
// * Reverts GL entries of the given allocated landed cost transaction.
// * @param {number} tenantId
// * @param {number} landedCostId
// * @param {Knex.Transaction} trx
// */
// public revertLandedCostGLEntries = async (
// landedCostId: number,
// trx: Knex.Transaction
// ) => {
// await this.journalService.revertJournalTransactions(
// landedCostId,
// 'LandedCost',
// trx
// );
// };
// }

View File

@@ -3,15 +3,14 @@ import {
IAllocatedLandedCostDeletedPayload,
} from '../types/BillLandedCosts.types';
import { OnEvent } from '@nestjs/event-emitter';
import { LandedCostGLEntries } from './LandedCostGLEntries.service';
// import { LandedCostGLEntries } from './LandedCostGLEntries.service';
import { Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
@Injectable()
export class LandedCostGLEntriesSubscriber {
constructor(
private readonly billLandedCostGLEntries: LandedCostGLEntries,
) {}
constructor() // private readonly billLandedCostGLEntries: LandedCostGLEntries,
{}
/**
* Writes GL entries once landed cost transaction created.
@@ -22,11 +21,11 @@ export class LandedCostGLEntriesSubscriber {
billLandedCost,
trx,
}: IAllocatedLandedCostCreatedPayload) {
await this.billLandedCostGLEntries.createLandedCostGLEntries(
billLandedCost.id,
trx
);
};
// await this.billLandedCostGLEntries.createLandedCostGLEntries(
// billLandedCost.id,
// trx
// );
}
/**
* Reverts GL entries associated to landed cost transaction once deleted.
@@ -37,9 +36,9 @@ export class LandedCostGLEntriesSubscriber {
oldBillLandedCost,
trx,
}: IAllocatedLandedCostDeletedPayload) {
await this.billLandedCostGLEntries.revertLandedCostGLEntries(
oldBillLandedCost.id,
trx
);
};
}
// await this.billLandedCostGLEntries.revertLandedCostGLEntries(
// oldBillLandedCost.id,
// trx
// );
}
}

View File

@@ -11,9 +11,8 @@ export class LandedCostSyncCostTransactions {
/**
* Allocate the landed cost amount to cost transactions.
* @param {number} tenantId -
* @param {string} transactionType
* @param {number} transactionId
* @param {string} transactionType - Transaction type.
* @param {number} transactionId - Transaction id.
*/
public incrementLandedCostAmount = async (
transactionType: string,
@@ -22,18 +21,18 @@ export class LandedCostSyncCostTransactions {
amount: number,
trx?: Knex.Transaction
): Promise<void> => {
const Model = this.transactionLandedCost.getModel(
const Model = await this.transactionLandedCost.getModel(
transactionType
);
const relation = CONFIG.COST_TYPES[transactionType].entries;
// Increment the landed cost transaction amount.
await Model.query(trx)
await Model().query(trx)
.where('id', transactionId)
.increment('allocatedCostAmount', amount);
// Increment the landed cost entry.
await Model.relatedQuery(relation, trx)
await Model().relatedQuery(relation, trx)
.for(transactionId)
.where('id', transactionEntryId)
.increment('allocatedCostAmount', amount);
@@ -54,18 +53,18 @@ export class LandedCostSyncCostTransactions {
amount: number,
trx?: Knex.Transaction
) => {
const Model = this.transactionLandedCost.getModel(
const Model = await this.transactionLandedCost.getModel(
transactionType
);
const relation = CONFIG.COST_TYPES[transactionType].entries;
// Decrement the allocate cost amount of cost transaction.
await Model.query(trx)
await Model().query(trx)
.where('id', transactionId)
.decrement('allocatedCostAmount', amount);
// Decrement the allocated cost amount cost transaction entry.
await Model.relatedQuery(relation, trx)
await Model().relatedQuery(relation, trx)
.for(transactionId)
.where('id', transactionEntryId)
.decrement('allocatedCostAmount', amount);

View File

@@ -1,70 +1,73 @@
import { Inject, Service } from 'typedi';
import { Injectable } from '@nestjs/common';
import { ref } from 'objection';
import { curry, pipe, map } from 'lodash/fp';
import * as R from 'ramda';
import {
ILandedCostTransactionsQueryDTO,
ILandedCostTransaction,
ILandedCostTransactionDOJO,
ILandedCostTransactionEntry,
ILandedCostTransactionEntryDOJO,
} from '@/interfaces';
import TransactionLandedCost from './TransctionLandedCost';
import { formatNumber } from 'utils';
} from '../types/BillLandedCosts.types';
import { TransactionLandedCost } from './TransctionLandedCost.service';
import { formatNumber } from '@/utils/format-number';
import { LandedCostTransactionsQueryDto } from '../dtos/LandedCostTransactionsQuery.dto';
@Service()
export default class LandedCostTranasctions {
@Inject()
private transactionLandedCost: TransactionLandedCost;
@Injectable()
export class LandedCostTranasctions {
constructor(private readonly transactionLandedCost: TransactionLandedCost) {}
/**
* Retrieve the landed costs based on the given query.
* @param {number} tenantId
* @param {ILandedCostTransactionsQueryDTO} query
* @param {LandedCostTransactionsQueryDto} query -
* @returns {Promise<ILandedCostTransaction[]>}
*/
public getLandedCostTransactions = async (
query: ILandedCostTransactionsQueryDTO
query: LandedCostTransactionsQueryDto,
): Promise<ILandedCostTransaction[]> => {
const { transactionType } = query;
const Model = this.transactionLandedCost.getModel(
query.transactionType
const Model = await this.transactionLandedCost.getModel(
query.transactionType,
);
// Retrieve the model entities.
const transactions = await Model.query().onBuild((q) => {
q.where('allocated_cost_amount', '<', ref('landed_cost_amount'));
const transactions = await Model()
.query()
.onBuild((q) => {
q.where('allocated_cost_amount', '<', ref('landed_cost_amount'));
if (query.transactionType === 'Bill') {
q.withGraphFetched('entries.item');
} else if (query.transactionType === 'Expense') {
q.withGraphFetched('categories.expenseAccount');
}
});
const transformLandedCost =
this.transactionLandedCost.transformToLandedCost(transactionType);
if (query.transactionType === 'Bill') {
q.withGraphFetched('entries.item');
} else if (query.transactionType === 'Expense') {
q.withGraphFetched('categories.expenseAccount');
}
});
const transformLandedCost = curry(
this.transactionLandedCost.transformToLandedCost,
)(transactionType);
return R.compose(
return pipe(
this.transformLandedCostTransactions,
R.map(transformLandedCost)
R.map(transformLandedCost),
)(transactions);
};
/**
*
* @param transactions
* @returns
* Transformes the landed cost transactions.
* @param {ILandedCostTransaction[]} transactions
* @returns {ILandedCostTransactionDOJO[]}
*/
public transformLandedCostTransactions = (
transactions: ILandedCostTransaction[]
transactions: ILandedCostTransaction[],
) => {
return R.map(this.transformLandedCostTransaction)(transactions);
};
/**
* Transformes the landed cost transaction.
* @param {ILandedCostTransaction} transaction
* @param {ILandedCostTransaction} transaction - Landed cost transaction.
* @returns {ILandedCostTransactionDOJO}
*/
public transformLandedCostTransaction = (
transaction: ILandedCostTransaction
transaction: ILandedCostTransaction,
): ILandedCostTransactionDOJO => {
const { currencyCode } = transaction;
@@ -74,57 +77,60 @@ export default class LandedCostTranasctions {
// Formatted transaction unallocated cost amount.
const formattedUnallocatedCostAmount = formatNumber(
transaction.unallocatedCostAmount,
{ currencyCode }
{ currencyCode },
);
// Formatted transaction allocated cost amount.
const formattedAllocatedCostAmount = formatNumber(
transaction.allocatedCostAmount,
{ currencyCode }
{ currencyCode },
);
const transformLandedCostEntry = R.curry(this.transformLandedCostEntry)(
transaction,
);
const entries = R.map<
ILandedCostTransactionEntry,
ILandedCostTransactionEntryDOJO
>(transformLandedCostEntry)(transaction.entries);
return {
...transaction,
formattedAmount,
formattedUnallocatedCostAmount,
formattedAllocatedCostAmount,
entries: R.map(this.transformLandedCostEntry(transaction))(
transaction.entries
),
entries,
};
};
/**
*
* @param {ILandedCostTransaction} transaction
* @param {ILandedCostTransactionEntry} entry
* Transformes the landed cost transaction entry.
* @param {ILandedCostTransaction} transaction - Landed cost transaction.
* @param {ILandedCostTransactionEntry} entry - Landed cost transaction entry.
* @returns {ILandedCostTransactionEntryDOJO}
*/
public transformLandedCostEntry = R.curry(
(
transaction: ILandedCostTransaction,
entry: ILandedCostTransactionEntry
): ILandedCostTransactionEntryDOJO => {
const { currencyCode } = transaction;
public transformLandedCostEntry = (
transaction: ILandedCostTransaction,
entry: ILandedCostTransactionEntry,
): ILandedCostTransactionEntryDOJO => {
const { currencyCode } = transaction;
// Formatted entry amount.
const formattedAmount = formatNumber(entry.amount, { currencyCode });
// Formatted entry amount.
const formattedAmount = formatNumber(entry.amount, { currencyCode });
// Formatted entry unallocated cost amount.
const formattedUnallocatedCostAmount = formatNumber(
entry.unallocatedCostAmount,
{ currencyCode }
);
// Formatted entry allocated cost amount.
const formattedAllocatedCostAmount = formatNumber(
entry.allocatedCostAmount,
{ currencyCode }
);
return {
...entry,
formattedAmount,
formattedUnallocatedCostAmount,
formattedAllocatedCostAmount,
};
}
);
}
// Formatted entry unallocated cost amount.
const formattedUnallocatedCostAmount = formatNumber(
entry.unallocatedCostAmount,
{ currencyCode },
);
// Formatted entry allocated cost amount.
const formattedAllocatedCostAmount = formatNumber(
entry.allocatedCostAmount,
{ currencyCode },
);
return {
...entry,
formattedAmount,
formattedUnallocatedCostAmount,
formattedAllocatedCostAmount,
};
};
}

View File

@@ -7,7 +7,6 @@ import { events } from '@/common/events/events';
import { IAllocatedLandedCostDeletedPayload } from '../types/BillLandedCosts.types';
import { BillLandedCostEntry } from '../models/BillLandedCostEntry';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { BillLandedCost } from '../models/BillLandedCost';
@Injectable()
export class RevertAllocatedLandedCost extends BaseLandedCostService {
@@ -15,11 +14,6 @@ export class RevertAllocatedLandedCost extends BaseLandedCostService {
private readonly eventPublisher: EventEmitter2,
private readonly uow: UnitOfWork,
@Inject(BillLandedCost.name)
private readonly billLandedCostModel: TenantModelProxy<
typeof BillLandedCost
>,
@Inject(BillLandedCostEntry.name)
private readonly billLandedCostEntryModel: TenantModelProxy<
typeof BillLandedCostEntry

View File

@@ -3,35 +3,46 @@ import { Model } from 'objection';
import {
ILandedCostTransaction,
ILandedCostTransactionEntry,
LandedCostTransactionModel,
LandedCostTransactionType,
} from '../types/BillLandedCosts.types';
import { Injectable } from '@nestjs/common';
import { BillLandedCost } from '../models/BillLandedCost';
import { Bill } from '@/modules/Bills/models/Bill';
import { Expense } from '@/modules/Expenses/models/Expense.model';
import { ServiceError } from '@/modules/Items/ServiceError';
import { ContextIdFactory, ModuleRef } from '@nestjs/core';
import { sanitizeModelName } from '@/utils/sanitize-model-name';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { ExpenseLandedCost } from './ExpenseLandedCost.service';
import { BillLandedCost } from './BillLandedCost.service';
import { ERRORS } from '../utils';
import { ExpenseLandedCost } from '../models/ExpenseLandedCost';
@Injectable()
export class TransactionLandedCost {
constructor(
private readonly billLandedCost: BillLandedCost,
private readonly expenseLandedCost: ExpenseLandedCost,
private readonly moduleRef: ModuleRef,
) {}
/**
* Retrieve the cost transaction code model.
* @param {number} tenantId - Tenant id.
* @param {string} transactionType - Transaction type.
* @returns
*/
public getModel = (tenantId: number, transactionType: string): Model => {
const Models = this.tenancy.models(tenantId);
const Model = Models[transactionType];
public getModel = async (
transactionType: string,
): Promise<TenantModelProxy<typeof Model>> => {
const contextId = ContextIdFactory.create();
const modelName = sanitizeModelName(transactionType);
if (!Model) {
const instance = await this.moduleRef.resolve(modelName, contextId, {
strict: false,
});
if (!instance) {
throw new ServiceError(ERRORS.COST_TYPE_UNDEFINED);
}
return Model;
return instance;
};
/**
@@ -40,10 +51,10 @@ export class TransactionLandedCost {
* @param {IBill|IExpense} transaction - Expense or bill transaction.
* @returns {ILandedCostTransaction}
*/
public transformToLandedCost = R.curry(
public transformToLandedCost =
(
transactionType: string,
transaction: Bill | Expense,
transactionType: LandedCostTransactionType,
transaction: LandedCostTransactionModel,
): ILandedCostTransaction => {
return R.compose(
R.when(
@@ -54,9 +65,8 @@ export class TransactionLandedCost {
R.always(transactionType === 'Expense'),
this.expenseLandedCost.transformToLandedCost,
),
)(transaction);
},
);
)(transaction) as ILandedCostTransaction;
};
/**
* Transformes the given expense or bill entry to landed cost transaction entry.
@@ -65,7 +75,7 @@ export class TransactionLandedCost {
* @returns {ILandedCostTransactionEntry}
*/
public transformToLandedCostEntry = (
transactionType: 'Bill' | 'Expense',
transactionType: LandedCostTransactionType,
transactionEntry,
): ILandedCostTransactionEntry => {
return R.compose(
@@ -77,6 +87,6 @@ export class TransactionLandedCost {
R.always(transactionType === 'Expense'),
this.expenseLandedCost.transformToLandedCostEntry,
),
)(transactionEntry);
)(transactionEntry) as ILandedCostTransactionEntry;
};
}