feat(nestjs): migrate to NestJS

This commit is contained in:
Ahmed Bouhuolia
2025-04-07 11:51:24 +02:00
parent f068218a16
commit 55fcc908ef
3779 changed files with 631 additions and 195332 deletions

View File

@@ -0,0 +1,33 @@
import { Inject, Injectable } from '@nestjs/common';
import { omit } from 'lodash';
import { BranchesSettingsService } from '../BranchesSettings';
@Injectable()
export class BranchTransactionDTOTransformer {
constructor(private readonly branchesSettings: BranchesSettingsService) {}
/**
* Excludes DTO branch id when mutli-warehouses feature is inactive.
* @returns {any}
*/
private excludeDTOBranchIdWhenInactive = async <
T extends { branchId?: number },
>(
DTO: T,
): Promise<Omit<T, 'branchId'> | T> => {
const isActive = await this.branchesSettings.isMultiBranchesActive();
return !isActive ? omit(DTO, ['branchId']) : DTO;
};
/**
* Transforms the input DTO for branches feature.
* @param {T} DTO -
* @returns {Omit<T, 'branchId'> | T}
*/
public transformDTO = async <T extends { branchId?: number }>(
DTO: T,
): Promise<Omit<T, 'branchId'> | T> => {
return this.excludeDTOBranchIdWhenInactive<T>(DTO);
};
}

View File

@@ -0,0 +1,28 @@
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { BankTransaction } from '@/modules/BankingTransactions/models/BankTransaction';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class CashflowTransactionsActivateBranches {
constructor(
@Inject(BankTransaction.name)
private readonly bankTransaction: TenantModelProxy<typeof BankTransaction>,
) {}
/**
* Updates all cashflow transactions with the primary branch.
* @param {number} primaryBranchId - The primary branch id.
* @param {Knex.Transaction} trx - The database transaction.
* @returns {Promise<void>}
*/
public updateCashflowTransactionsWithBranch = async (
primaryBranchId: number,
trx?: Knex.Transaction,
) => {
// Updates the cashflow transactions with primary branch.
await this.bankTransaction()
.query(trx)
.update({ branchId: primaryBranchId });
};
}

View File

@@ -0,0 +1,25 @@
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { Expense } from '@/modules/Expenses/models/Expense.model';
@Injectable()
export class ExpensesActivateBranches {
constructor(
@Inject(Expense.name)
private readonly expenseModel: TenantModelProxy<typeof Expense>,
) {}
/**
* Updates all expenses transactions with the primary branch.
* @param {number} primaryBranchId
* @returns {Promise<void>}
*/
public updateExpensesWithBranch = async (
primaryBranchId: number,
trx?: Knex.Transaction,
) => {
// Updates the expenses with primary branch.
await this.expenseModel().query(trx).update({ branchId: primaryBranchId });
};
}

View File

@@ -0,0 +1,26 @@
import { Knex } from 'knex';
import { Injectable } from '@nestjs/common';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { ManualJournal } from '@/modules/ManualJournals/models/ManualJournal';
@Injectable()
export class ManualJournalsActivateBranches {
constructor(
private readonly manualJournalModel: TenantModelProxy<typeof ManualJournal>,
) {}
/**
* Updates all manual journals transactions with the primary branch.
* @param {number} primaryBranchId
* @returns {Promise<void>}
*/
public updateManualJournalsWithBranch = async (
primaryBranchId: number,
trx?: Knex.Transaction,
) => {
// Updates the manual journal with primary branch.
await this.manualJournalModel()
.query(trx)
.update({ branchId: primaryBranchId });
};
}

View File

@@ -0,0 +1,40 @@
import { Inject, Injectable } from '@nestjs/common';
import { omit } from 'lodash';
import { BranchesSettingsService } from '../../BranchesSettings';
import { IManualJournalDTO } from '@/modules/ManualJournals/types/ManualJournals.types';
@Injectable()
export class ManualJournalBranchesDTOTransformer {
constructor(
@Inject()
private readonly branchesSettings: BranchesSettingsService,
) {}
/**
*
* @param DTO
* @returns
*/
private excludeDTOBranchIdWhenInactive = async (
DTO: IManualJournalDTO,
): Promise<IManualJournalDTO> => {
const isActive = await this.branchesSettings.isMultiBranchesActive();
if (isActive) {
return DTO;
}
return {
...DTO,
entries: DTO.entries.map((e) => omit(e, ['branchId'])),
};
};
/**
*
*/
public transformDTO = async (
DTO: IManualJournalDTO,
): Promise<IManualJournalDTO> => {
return this.excludeDTOBranchIdWhenInactive(DTO);
};
}

View File

@@ -0,0 +1,24 @@
import { IManualJournalDTO } from '@/modules/ManualJournals/types/ManualJournals.types';
import { IManualJournalEntryDTO } from '@/modules/ManualJournals/types/ManualJournals.types';
import { ERRORS } from './constants';
import { Injectable } from '@nestjs/common';
import { ServiceError } from '@/modules/Items/ServiceError';
@Injectable()
export class ManualJournalBranchesValidator {
/**
* Validates the DTO entries should have branch id.
* @param {IManualJournalDTO} manualJournalDTO
*/
public validateEntriesHasBranchId = async (
manualJournalDTO: IManualJournalDTO,
) => {
const hasNoIdEntries = manualJournalDTO.entries.filter(
(entry: IManualJournalEntryDTO) =>
!entry.branchId && !manualJournalDTO.branchId,
);
if (hasNoIdEntries.length > 0) {
throw new ServiceError(ERRORS.MANUAL_JOURNAL_ENTRIES_HAVE_NO_BRANCH_ID);
}
};
}

View File

@@ -0,0 +1,4 @@
export const ERRORS = {
MANUAL_JOURNAL_ENTRIES_HAVE_NO_BRANCH_ID:
'MANUAL_JOURNAL_ENTRIES_HAVE_NO_BRANCH_ID',
};

View File

@@ -0,0 +1,23 @@
import { Knex } from 'knex';
import { Injectable } from '@nestjs/common';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { Bill } from '@/modules/Bills/models/Bill';
@Injectable()
export class BillActivateBranches {
constructor(private readonly billModel: TenantModelProxy<typeof Bill>) {}
/**
* Updates all bills transactions with the primary branch.
* @param {number} tenantId
* @param {number} primaryBranchId
* @returns {Promise<void>}
*/
public updateBillsWithBranch = async (
primaryBranchId: number,
trx?: Knex.Transaction,
) => {
// Updates the sale invoice with primary branch.
await Bill.query(trx).update({ branchId: primaryBranchId });
};
}

View File

@@ -0,0 +1,24 @@
import { Knex } from 'knex';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { BillPayment } from '@/modules/BillPayments/models/BillPayment';
import { Injectable } from '@nestjs/common';
@Injectable()
export class BillPaymentsActivateBranches {
constructor(
private readonly billPaymentModel: TenantModelProxy<typeof BillPayment>,
) {}
/**
* Updates all bills payments transcations with the primary branch.
* @param {number} primaryBranchId
* @returns {Promise<void>}
*/
public updateBillPaymentsWithBranch = async (
primaryBranchId: number,
trx?: Knex.Transaction
) => {
// Updates the bill payments with primary branch.
await this.billPaymentModel().query(trx).update({ branchId: primaryBranchId });
};
}

View File

@@ -0,0 +1,27 @@
import { Knex } from 'knex';
import { Injectable } from '@nestjs/common';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { VendorCredit } from '@/modules/VendorCredit/models/VendorCredit';
@Injectable()
export class VendorCreditActivateBranches {
constructor(
private readonly vendorCreditModel: TenantModelProxy<typeof VendorCredit>,
) {}
/**
* Updates all vendor credits transcations with the primary branch.
* @param {number} tenantId
* @param {number} primaryBranchId
* @returns {Promise<void>}
*/
public updateVendorCreditsWithBranch = async (
primaryBranchId: number,
trx?: Knex.Transaction,
) => {
// Updates the vendors credits with primary branch.
await this.vendorCreditModel()
.query(trx)
.update({ branchId: primaryBranchId });
};
}

View File

@@ -0,0 +1,24 @@
import { Knex } from 'knex';
import { CreditNote } from '@/modules/CreditNotes/models/CreditNote';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { Injectable } from '@nestjs/common';
@Injectable()
export class CreditNoteActivateBranches {
constructor(
private readonly creditNoteModel: TenantModelProxy<typeof CreditNote>,
) {}
/**
* Updates all creidt notes transactions with the primary branch.
* @param {number} primaryBranchId
* @returns {Promise<void>}
*/
public updateCreditsWithBranch = async (
primaryBranchId: number,
trx?: Knex.Transaction
) => {
// Updates the sale invoice with primary branch.
await this.creditNoteModel().query(trx).update({ branchId: primaryBranchId });
};
}

View File

@@ -0,0 +1,24 @@
import { PaymentReceived } from '@/modules/PaymentReceived/models/PaymentReceived';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { Injectable } from '@nestjs/common';
import { Knex } from 'knex';
@Injectable()
export class PaymentReceiveActivateBranches {
constructor(
private readonly paymentReceivedModel: TenantModelProxy<typeof PaymentReceived>,
) {}
/**
* Updates all creidt notes transactions with the primary branch.
* @param {number} primaryBranchId
* @returns {Promise<void>}
*/
public updatePaymentsWithBranch = async (
primaryBranchId: number,
trx?: Knex.Transaction
) => {
// Updates the sale invoice with primary branch.
await this.paymentReceivedModel().query(trx).update({ branchId: primaryBranchId });
};
}

View File

@@ -0,0 +1,25 @@
import { Knex } from 'knex';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { PaymentReceived } from '@/modules/PaymentReceived/models/PaymentReceived';
import { Inject, Injectable } from '@nestjs/common';
@Injectable()
export class SaleEstimateActivateBranches {
constructor(
@Inject(PaymentReceived.name)
private readonly paymentReceivedModel: TenantModelProxy<typeof PaymentReceived>,
) {}
/**
* Updates all sale estimates transactions with the primary branch.
* @param {number} primaryBranchId
* @returns {Promise<void>}
*/
public updateEstimatesWithBranch = async (
primaryBranchId: number,
trx?: Knex.Transaction
) => {
// Updates the sale invoice with primary branch.
await this.paymentReceivedModel().query(trx).update({ branchId: primaryBranchId });
};
}

View File

@@ -0,0 +1,26 @@
import { Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class SaleInvoiceActivateBranches {
constructor(
private readonly saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
) {}
/**
* Updates all sale invoices transactions with the primary branch.
* @param {number} primaryBranchId
* @returns {Promise<void>}
*/
public updateInvoicesWithBranch = async (
primaryBranchId: number,
trx?: Knex.Transaction,
) => {
// Updates the sale invoice with primary branch.
await this.saleInvoiceModel()
.query(trx)
.update({ branchId: primaryBranchId });
};
}

View File

@@ -0,0 +1,26 @@
import { Knex } from 'knex';
import { Injectable } from '@nestjs/common';
import { SaleReceipt } from '@/modules/SaleReceipts/models/SaleReceipt';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class SaleReceiptActivateBranches {
constructor(
private readonly saleReceiptModel: TenantModelProxy<typeof SaleReceipt>,
) {}
/**
* Updates all sale receipts transactions with the primary branch.
* @param {number} primaryBranchId
* @returns {Promise<void>}
*/
public updateReceiptsWithBranch = async (
primaryBranchId: number,
trx?: Knex.Transaction,
) => {
// Updates the sale receipt with primary branch.
await this.saleReceiptModel()
.query(trx)
.update({ branchId: primaryBranchId });
};
}

View File

@@ -0,0 +1,65 @@
import { ServiceError } from '@/modules/Items/ServiceError';
import { BranchesSettingsService } from '../BranchesSettings';
import { ERRORS } from './constants';
import { Inject, Injectable } from '@nestjs/common';
import { Branch } from '../models/Branch.model';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class ValidateBranchExistance {
constructor(
private readonly branchesSettings: BranchesSettingsService,
@Inject(Branch.name)
private readonly branchModel: TenantModelProxy<typeof Branch>,
) {}
/**
* Validate transaction branch id when the feature is active.
* @param {number} branchId
* @returns {Promise<void>}
*/
public validateTransactionBranchWhenActive = async (
branchId: number | null,
) => {
const isActive = this.branchesSettings.isMultiBranchesActive();
// Can't continue if the multi-warehouses feature is inactive.
if (!isActive) return;
return this.validateTransactionBranch(branchId);
};
/**
* Validate transaction branch id existance.
* @param {number} branchId
* @return {Promise<void>}
*/
public validateTransactionBranch = async (branchId: number | null) => {
this.validateBranchIdExistance(branchId);
await this.validateBranchExistance(branchId);
};
/**
* Validates the branch id existance.
* @param {number} branchId
*/
public validateBranchIdExistance = (branchId: number | null) => {
if (!branchId) {
throw new ServiceError(ERRORS.BRANCH_ID_REQUIRED);
}
};
/**
* Validates the branch id existance.
* @param {number} branchId
*/
public validateBranchExistance = async (branchId: number) => {
const branch = await this.branchModel().query().findById(branchId);
if (!branch) {
throw new ServiceError(ERRORS.BRANCH_ID_NOT_FOUND);
}
};
}

View File

@@ -0,0 +1,6 @@
export const ERRORS = {
BRANCH_ID_REQUIRED: 'BRANCH_ID_REQUIRED',
BRANCH_ID_NOT_FOUND: 'BRANCH_ID_NOT_FOUND'
}