refactor: sale estimates to nestjs

This commit is contained in:
Ahmed Bouhuolia
2024-12-22 14:16:01 +02:00
parent 8a12caf48d
commit 336171081e
69 changed files with 4998 additions and 497 deletions

View File

@@ -0,0 +1,208 @@
import { Knex } from 'knex';
import { Expense } from './models/Expense.model';
import { SystemUser } from '../System/models/SystemUser';
import { IFilterRole } from '../DynamicListing/DynamicFilter/DynamicFilter.types';
// import { ISystemUser } from './User';
// import { IFilterRole } from './DynamicFilter';
// import { IAccount } from './Account';
// import { AttachmentLinkDTO } from './Attachments';
export interface IPaginationMeta {
total: number;
page: number;
pageSize: number;
}
export interface IExpensesFilter {
page: number;
pageSize: number;
filterRoles?: IFilterRole[];
columnSortBy: string;
sortOrder: string;
viewSlug?: string;
filterQuery?: (query: any) => void;
}
// export interface IExpense {
// id: number;
// totalAmount: number;
// localAmount?: number;
// currencyCode: string;
// exchangeRate: number;
// description?: string;
// paymentAccountId: number;
// peyeeId?: number;
// referenceNo?: string;
// publishedAt: Date | null;
// userId: number;
// paymentDate: Date;
// payeeId: number;
// landedCostAmount: number;
// allocatedCostAmount: number;
// unallocatedCostAmount: number;
// categories?: IExpenseCategory[];
// isPublished: boolean;
// localLandedCostAmount?: number;
// localAllocatedCostAmount?: number;
// localUnallocatedCostAmount?: number;
// billableAmount: number;
// invoicedAmount: number;
// branchId?: number;
// createdAt?: Date;
// }
// export interface IExpenseCategory {
// id?: number;
// expenseAccountId: number;
// index: number;
// description: string;
// expenseId: number;
// amount: number;
// projectId?: number;
// allocatedCostAmount: number;
// unallocatedCostAmount: number;
// landedCost: boolean;
// expenseAccount?: IAccount;
// }
export interface IExpenseCommonDTO {
currencyCode: string;
exchangeRate?: number;
description?: string;
paymentAccountId: number;
peyeeId?: number;
referenceNo?: string;
publish: boolean;
userId: number;
paymentDate: Date;
payeeId: number;
categories: IExpenseCategoryDTO[];
branchId?: number;
// attachments?: AttachmentLinkDTO[];
}
export interface IExpenseCreateDTO extends IExpenseCommonDTO {}
export interface IExpenseEditDTO extends IExpenseCommonDTO {}
export interface IExpenseCategoryDTO {
id?: number;
expenseAccountId: number;
index: number;
amount: number;
description?: string;
expenseId: number;
landedCost?: boolean;
projectId?: number;
}
// export interface IExpensesService {
// newExpense(
// tenantid: number,
// expenseDTO: IExpenseDTO,
// authorizedUser: ISystemUser
// ): Promise<IExpense>;
// editExpense(
// tenantid: number,
// expenseId: number,
// expenseDTO: IExpenseDTO,
// authorizedUser: ISystemUser
// ): void;
// publishExpense(
// tenantId: number,
// expenseId: number,
// authorizedUser: ISystemUser
// ): Promise<void>;
// deleteExpense(
// tenantId: number,
// expenseId: number,
// authorizedUser: ISystemUser
// ): Promise<void>;
// getExpensesList(
// tenantId: number,
// expensesFilter: IExpensesFilter
// ): Promise<{
// expenses: IExpense[];
// pagination: IPaginationMeta;
// filterMeta: IFilterMeta;
// }>;
// getExpense(tenantId: number, expenseId: number): Promise<IExpense>;
// }
export interface IExpenseCreatingPayload {
trx: Knex.Transaction;
// tenantId: number;
expenseDTO: IExpenseCreateDTO;
}
export interface IExpenseEventEditingPayload {
// tenantId: number;
oldExpense: Expense;
expenseDTO: IExpenseEditDTO;
trx: Knex.Transaction;
}
export interface IExpenseCreatedPayload {
// tenantId: number;
expenseId: number;
// authorizedUser: ISystemUser;
expense: Expense;
expenseDTO: IExpenseCreateDTO;
trx?: Knex.Transaction;
}
export interface IExpenseEventEditPayload {
// tenantId: number;
expenseId: number;
expense: Expense;
expenseDTO: IExpenseEditDTO;
authorizedUser: SystemUser;
oldExpense: Expense;
trx: Knex.Transaction;
}
export interface IExpenseEventDeletePayload {
// tenantId: number;
expenseId: number;
authorizedUser: SystemUser;
oldExpense: Expense;
trx: Knex.Transaction;
}
export interface IExpenseDeletingPayload {
trx: Knex.Transaction;
// tenantId: number;
oldExpense: Expense;
}
export interface IExpenseEventPublishedPayload {
// tenantId: number;
expenseId: number;
oldExpense: Expense;
expense: Expense;
authorizedUser: SystemUser;
trx: Knex.Transaction;
}
export interface IExpensePublishingPayload {
trx: Knex.Transaction;
oldExpense: Expense;
// tenantId: number;
}
export enum ExpenseAction {
Create = 'Create',
Edit = 'Edit',
Delete = 'Delete',
View = 'View',
}

View File

@@ -1,113 +1,113 @@
import * as R from 'ramda';
import {
AccountNormal,
IExpenseCategory,
ILedger,
ILedgerEntry,
} from '@/interfaces';
import Ledger from '../Accounting/Ledger';
// import * as R from 'ramda';
// import {
// AccountNormal,
// IExpenseCategory,
// ILedger,
// ILedgerEntry,
// } from '@/interfaces';
// import Ledger from '../Accounting/Ledger';
export class ExpenseGL {
private expense: any;
// export class ExpenseGL {
// private expense: any;
/**
* Constructor method.
*/
constructor(expense: any) {
this.expense = expense;
}
// /**
// * Constructor method.
// */
// constructor(expense: any) {
// this.expense = expense;
// }
/**
* Retrieves the expense GL common entry.
* @param {IExpense} expense
* @returns {Partial<ILedgerEntry>}
*/
private getExpenseGLCommonEntry = (): Partial<ILedgerEntry> => {
return {
currencyCode: this.expense.currencyCode,
exchangeRate: this.expense.exchangeRate,
// /**
// * Retrieves the expense GL common entry.
// * @param {IExpense} expense
// * @returns {Partial<ILedgerEntry>}
// */
// private getExpenseGLCommonEntry = (): Partial<ILedgerEntry> => {
// return {
// currencyCode: this.expense.currencyCode,
// exchangeRate: this.expense.exchangeRate,
transactionType: 'Expense',
transactionId: this.expense.id,
// transactionType: 'Expense',
// transactionId: this.expense.id,
date: this.expense.paymentDate,
userId: this.expense.userId,
// date: this.expense.paymentDate,
// userId: this.expense.userId,
debit: 0,
credit: 0,
// debit: 0,
// credit: 0,
branchId: this.expense.branchId,
};
};
// branchId: this.expense.branchId,
// };
// };
/**
* Retrieves the expense GL payment entry.
* @param {IExpense} expense
* @returns {ILedgerEntry}
*/
private getExpenseGLPaymentEntry = (): ILedgerEntry => {
const commonEntry = this.getExpenseGLCommonEntry();
// /**
// * Retrieves the expense GL payment entry.
// * @param {IExpense} expense
// * @returns {ILedgerEntry}
// */
// private getExpenseGLPaymentEntry = (): ILedgerEntry => {
// const commonEntry = this.getExpenseGLCommonEntry();
return {
...commonEntry,
credit: this.expense.localAmount,
accountId: this.expense.paymentAccountId,
accountNormal:
this.expense?.paymentAccount?.accountNormal === 'debit'
? AccountNormal.DEBIT
: AccountNormal.CREDIT,
index: 1,
};
};
// return {
// ...commonEntry,
// credit: this.expense.localAmount,
// accountId: this.expense.paymentAccountId,
// accountNormal:
// this.expense?.paymentAccount?.accountNormal === 'debit'
// ? AccountNormal.DEBIT
// : AccountNormal.CREDIT,
// index: 1,
// };
// };
/**
* Retrieves the expense GL category entry.
* @param {IExpense} expense -
* @param {IExpenseCategory} expenseCategory -
* @param {number} index
* @returns {ILedgerEntry}
*/
private getExpenseGLCategoryEntry = R.curry(
(category: IExpenseCategory, index: number): ILedgerEntry => {
const commonEntry = this.getExpenseGLCommonEntry();
const localAmount = category.amount * this.expense.exchangeRate;
// /**
// * Retrieves the expense GL category entry.
// * @param {IExpense} expense -
// * @param {IExpenseCategory} expenseCategory -
// * @param {number} index
// * @returns {ILedgerEntry}
// */
// private getExpenseGLCategoryEntry = R.curry(
// (category: IExpenseCategory, index: number): ILedgerEntry => {
// const commonEntry = this.getExpenseGLCommonEntry();
// const localAmount = category.amount * this.expense.exchangeRate;
return {
...commonEntry,
accountId: category.expenseAccountId,
accountNormal: AccountNormal.DEBIT,
debit: localAmount,
note: category.description,
index: index + 2,
projectId: category.projectId,
};
}
);
// return {
// ...commonEntry,
// accountId: category.expenseAccountId,
// accountNormal: AccountNormal.DEBIT,
// debit: localAmount,
// note: category.description,
// index: index + 2,
// projectId: category.projectId,
// };
// }
// );
/**
* Retrieves the expense GL entries.
* @param {IExpense} expense
* @returns {ILedgerEntry[]}
*/
public getExpenseGLEntries = (): ILedgerEntry[] => {
const getCategoryEntry = this.getExpenseGLCategoryEntry();
// /**
// * Retrieves the expense GL entries.
// * @param {IExpense} expense
// * @returns {ILedgerEntry[]}
// */
// public getExpenseGLEntries = (): ILedgerEntry[] => {
// const getCategoryEntry = this.getExpenseGLCategoryEntry();
const paymentEntry = this.getExpenseGLPaymentEntry();
const categoryEntries = this.expense.categories.map(getCategoryEntry);
// const paymentEntry = this.getExpenseGLPaymentEntry();
// const categoryEntries = this.expense.categories.map(getCategoryEntry);
return [paymentEntry, ...categoryEntries];
};
// return [paymentEntry, ...categoryEntries];
// };
/**
* Retrieves the given expense ledger.
* @param {IExpense} expense
* @returns {ILedger}
*/
public getExpenseLedger = (): ILedger => {
const entries = this.getExpenseGLEntries();
// /**
// * Retrieves the given expense ledger.
// * @param {IExpense} expense
// * @returns {ILedger}
// */
// public getExpenseLedger = (): ILedger => {
// const entries = this.getExpenseGLEntries();
console.log(entries, 'entries');
// console.log(entries, 'entries');
return new Ledger(entries);
};
}
// return new Ledger(entries);
// };
// }

View File

@@ -0,0 +1,42 @@
// import { Knex } from 'knex';
// import { ExpenseGL } from './ExpenseGL';
// import { Inject, Injectable } from '@nestjs/common';
// import { Expense } from '../models/Expense.model';
// @Injectable()
// export class ExpenseGLEntries {
// constructor(
// @Inject(Expense.name)
// private readonly expense: typeof Expense,
// ) {}
// /**
// * Retrieves the expense G/L of the given id.
// * @param {number} expenseId
// * @param {Knex.Transaction} trx
// * @returns {Promise<ILedger>}
// */
// public getExpenseLedgerById = async (
// expenseId: number,
// trx?: Knex.Transaction,
// ): Promise<ILedger> => {
// const expense = await this.expense
// .query(trx)
// .findById(expenseId)
// .withGraphFetched('categories')
// .withGraphFetched('paymentAccount')
// .throwIfNotFound();
// return this.getExpenseLedger(expense);
// };
// /**
// * Retrieves the given expense ledger.
// * @param {IExpense} expense
// * @returns {ILedger}
// */
// public getExpenseLedger = (expense: Expense): ILedger => {
// const expenseGL = new ExpenseGL(expense);
// return expenseGL.getExpenseLedger();
// };
// }

View File

@@ -0,0 +1,77 @@
// import {
// IExpenseCreatedPayload,
// IExpenseEventDeletePayload,
// IExpenseEventEditPayload,
// IExpenseEventPublishedPayload,
// } from '../Expenses.types';
// import { ExpenseGLEntriesStorage } from './ExpenseGLEntriesStorage';
// import { Injectable } from '@nestjs/common';
// import { OnEvent } from '@nestjs/event-emitter';
// import { events } from '@/common/events/events';
// @Injectable()
// export class ExpensesWriteGLSubscriber {
// /**
// * @param {ExpenseGLEntriesStorage} expenseGLEntries -
// */
// constructor(private readonly expenseGLEntries: ExpenseGLEntriesStorage) {}
// /**
// * Handles the writing journal entries once the expense created.
// * @param {IExpenseCreatedPayload} payload -
// */
// @OnEvent(events.expenses.onCreated)
// public async handleWriteGLEntriesOnceCreated({
// expense,
// trx,
// }: IExpenseCreatedPayload) {
// // In case expense published, write journal entries.
// if (!expense.publishedAt) return;
// await this.expenseGLEntries.writeExpenseGLEntries(expense.id, trx);
// }
// /**
// * Handle writing expense journal entries once the expense edited.
// * @param {IExpenseEventEditPayload} payload -
// */
// @OnEvent(events.expenses.onEdited)
// public async handleRewriteGLEntriesOnceEdited({
// expenseId,
// expense,
// authorizedUser,
// trx,
// }: IExpenseEventEditPayload) {
// // Cannot continue if the expense is not published.
// if (!expense.publishedAt) return;
// await this.expenseGLEntries.rewriteExpenseGLEntries(expense.id, trx);
// }
// /**
// * Reverts expense journal entries once the expense deleted.
// * @param {IExpenseEventDeletePayload} payload -
// */
// @OnEvent(events.expenses.onDeleted)
// public async handleRevertGLEntriesOnceDeleted({
// expenseId,
// trx,
// }: IExpenseEventDeletePayload) {
// await this.expenseGLEntries.revertExpenseGLEntries(expenseId, trx);
// }
// /**
// * Handles writing expense journal once the expense publish.
// * @param {IExpenseEventPublishedPayload} payload -
// */
// @OnEvent(events.expenses.onPublished)
// public async handleWriteGLEntriesOncePublished({
// expense,
// trx,
// }: IExpenseEventPublishedPayload) {
// // In case expense published, write journal entries.
// if (!expense.publishedAt) return;
// await this.expenseGLEntries.rewriteExpenseGLEntries(expense.id, trx);
// }
// }

View File

@@ -1,45 +0,0 @@
import { Knex } from 'knex';
import { Inject, Service } from 'typedi';
import { IExpense, ILedger } from '@/interfaces';
import { ExpenseGL } from './ExpenseGL';
import HasTenancyService from '../Tenancy/TenancyService';
@Service()
export class ExpenseGLEntries {
@Inject()
private tenancy: HasTenancyService;
/**
* Retrieves the expense G/L of the given id.
* @param {number} tenantId
* @param {number} expenseId
* @param {Knex.Transaction} trx
* @returns {Promise<ILedger>}
*/
public getExpenseLedgerById = async (
tenantId: number,
expenseId: number,
trx?: Knex.Transaction
): Promise<ILedger> => {
const { Expense } = await this.tenancy.models(tenantId);
const expense = await Expense.query(trx)
.findById(expenseId)
.withGraphFetched('categories')
.withGraphFetched('paymentAccount')
.throwIfNotFound();
return this.getExpenseLedger(expense);
};
/**
* Retrieves the given expense ledger.
* @param {IExpense} expense
* @returns {ILedger}
*/
public getExpenseLedger = (expense: IExpense): ILedger => {
const expenseGL = new ExpenseGL(expense);
return expenseGL.getExpenseLedger();
};
}

View File

@@ -1,72 +1,67 @@
import { Knex } from 'knex';
import { Service, Inject } from 'typedi';
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { ExpenseGLEntries } from './ExpenseGLEntriesService';
// import { Knex } from 'knex';
// import { ExpenseGLEntries } from './ExpenseGLEntries.service';
// import { Injectable } from '@nestjs/common';
@Service()
export class ExpenseGLEntriesStorage {
@Inject()
private expenseGLEntries: ExpenseGLEntries;
// @Injectable()
// export class ExpenseGLEntriesStorage {
// /**
// * @param {ExpenseGLEntries} expenseGLEntries
// * @param {LedgerStorageService} ledgerStorage
// */
// constructor(
// private readonly expenseGLEntries: ExpenseGLEntries,
// private readonly ledgerStorage: LedgerStorageService,
// ) {}
@Inject()
private ledgerStorage: LedgerStorageService;
// /**
// * Writes the expense GL entries.
// * @param {number} tenantId
// * @param {number} expenseId
// * @param {Knex.Transaction} trx
// */
// public writeExpenseGLEntries = async (
// expenseId: number,
// trx?: Knex.Transaction,
// ) => {
// // Retrieves the given expense ledger.
// const expenseLedger = await this.expenseGLEntries.getExpenseLedgerById(
// expenseId,
// trx,
// );
// // Commits the expense ledger entries.
// await this.ledgerStorage.commit(expenseLedger, trx);
// };
/**
* Writes the expense GL entries.
* @param {number} tenantId
* @param {number} expenseId
* @param {Knex.Transaction} trx
*/
public writeExpenseGLEntries = async (
tenantId: number,
expenseId: number,
trx?: Knex.Transaction
) => {
// Retrieves the given expense ledger.
const expenseLedger = await this.expenseGLEntries.getExpenseLedgerById(
tenantId,
expenseId,
trx
);
// Commits the expense ledger entries.
await this.ledgerStorage.commit(tenantId, expenseLedger, trx);
};
// /**
// * Reverts the given expense GL entries.
// * @param {number} tenantId
// * @param {number} expenseId
// * @param {Knex.Transaction} trx
// */
// public revertExpenseGLEntries = async (
// expenseId: number,
// trx?: Knex.Transaction,
// ) => {
// await this.ledgerStorage.deleteByReference(
// expenseId,
// 'Expense',
// trx,
// );
// };
/**
* Reverts the given expense GL entries.
* @param {number} tenantId
* @param {number} expenseId
* @param {Knex.Transaction} trx
*/
public revertExpenseGLEntries = async (
tenantId: number,
expenseId: number,
trx?: Knex.Transaction
) => {
await this.ledgerStorage.deleteByReference(
tenantId,
expenseId,
'Expense',
trx
);
};
// /**
// * Rewrites the expense GL entries.
// * @param {number} expenseId
// * @param {Knex.Transaction} trx
// */
// public rewriteExpenseGLEntries = async (
// expenseId: number,
// trx?: Knex.Transaction,
// ) => {
// // Reverts the expense GL entries.
// await this.revertExpenseGLEntries(expenseId, trx);
/**
* Rewrites the expense GL entries.
* @param {number} tenantId
* @param {number} expenseId
* @param {Knex.Transaction} trx
*/
public rewriteExpenseGLEntries = async (
tenantId: number,
expenseId: number,
trx?: Knex.Transaction
) => {
// Reverts the expense GL entries.
await this.revertExpenseGLEntries(tenantId, expenseId, trx);
// Writes the expense GL entries.
await this.writeExpenseGLEntries(tenantId, expenseId, trx);
};
}
// // Writes the expense GL entries.
// await this.writeExpenseGLEntries(expenseId, trx);
// };
// }

View File

@@ -1,117 +0,0 @@
import { Inject, Service } from 'typedi';
import events from '@/subscribers/events';
import TenancyService from '@/services/Tenancy/TenancyService';
import {
IExpenseCreatedPayload,
IExpenseEventDeletePayload,
IExpenseEventEditPayload,
IExpenseEventPublishedPayload,
} from '@/interfaces';
import { ExpenseGLEntriesStorage } from './ExpenseGLEntriesStorage';
@Service()
export class ExpensesWriteGLSubscriber {
@Inject()
private tenancy: TenancyService;
@Inject()
private expenseGLEntries: ExpenseGLEntriesStorage;
/**
* Attaches events with handlers.
* @param bus
*/
public attach(bus) {
bus.subscribe(
events.expenses.onCreated,
this.handleWriteGLEntriesOnceCreated
);
bus.subscribe(
events.expenses.onEdited,
this.handleRewriteGLEntriesOnceEdited
);
bus.subscribe(
events.expenses.onDeleted,
this.handleRevertGLEntriesOnceDeleted
);
bus.subscribe(
events.expenses.onPublished,
this.handleWriteGLEntriesOncePublished
);
}
/**
* Handles the writing journal entries once the expense created.
* @param {IExpenseCreatedPayload} payload -
*/
public handleWriteGLEntriesOnceCreated = async ({
expense,
tenantId,
trx,
}: IExpenseCreatedPayload) => {
// In case expense published, write journal entries.
if (!expense.publishedAt) return;
await this.expenseGLEntries.writeExpenseGLEntries(
tenantId,
expense.id,
trx
);
};
/**
* Handle writing expense journal entries once the expense edited.
* @param {IExpenseEventEditPayload} payload -
*/
public handleRewriteGLEntriesOnceEdited = async ({
expenseId,
tenantId,
expense,
authorizedUser,
trx,
}: IExpenseEventEditPayload) => {
// Cannot continue if the expense is not published.
if (!expense.publishedAt) return;
await this.expenseGLEntries.rewriteExpenseGLEntries(
tenantId,
expense.id,
trx
);
};
/**
* Reverts expense journal entries once the expense deleted.
* @param {IExpenseEventDeletePayload} payload -
*/
public handleRevertGLEntriesOnceDeleted = async ({
expenseId,
tenantId,
trx,
}: IExpenseEventDeletePayload) => {
await this.expenseGLEntries.revertExpenseGLEntries(
tenantId,
expenseId,
trx
);
};
/**
* Handles writing expense journal once the expense publish.
* @param {IExpenseEventPublishedPayload} payload -
*/
public handleWriteGLEntriesOncePublished = async ({
tenantId,
expense,
trx,
}: IExpenseEventPublishedPayload) => {
// In case expense published, write journal entries.
if (!expense.publishedAt) return;
await this.expenseGLEntries.rewriteExpenseGLEntries(
tenantId,
expense.id,
trx
);
};
}