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,35 @@
// import { Request, Response, NextFunction } from 'express';
// import { ServiceError } from '@/exceptions';
// /**
// * Handles branches integration service errors.
// * @param {Error} error
// * @param {Request} req
// * @param {Response} res
// * @param {NextFunction} next
// */
// export function BranchIntegrationErrorsMiddleware(
// error: Error,
// req: Request,
// res: Response,
// next: NextFunction
// ) {
// if (error instanceof ServiceError) {
// if (error.errorType === 'WAREHOUSE_ID_NOT_FOUND') {
// return res.boom.badRequest(null, {
// errors: [{ type: 'WAREHOUSE_ID_NOT_FOUND', code: 5000 }],
// });
// }
// if (error.errorType === 'BRANCH_ID_REQUIRED') {
// return res.boom.badRequest(null, {
// errors: [{ type: 'BRANCH_ID_REQUIRED', code: 5100 }],
// });
// }
// if (error.errorType === 'BRANCH_ID_NOT_FOUND') {
// return res.boom.badRequest(null, {
// errors: [{ type: 'BRANCH_ID_NOT_FOUND', code: 5300 }],
// });
// }
// }
// next(error);
// }

View File

@@ -0,0 +1,93 @@
import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
} from '@nestjs/common';
import { BranchesApplication } from './BranchesApplication.service';
import { CreateBranchDto, EditBranchDto } from './dtos/Branch.dto';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
@Controller('branches')
@ApiTags('branches')
export class BranchesController {
constructor(private readonly branchesApplication: BranchesApplication) {}
@Get()
@ApiOperation({ summary: 'Retrieves the branches.' })
@ApiResponse({
status: 200,
description: 'The branches have been successfully retrieved.',
})
getBranches() {
return this.branchesApplication.getBranches();
}
@Get(':id')
@ApiOperation({ summary: 'Retrieves the branch details.' })
@ApiResponse({
status: 200,
description: 'The branch details have been successfully retrieved.',
})
@ApiResponse({ status: 404, description: 'The branch not found.' })
getBranch(@Param('id') id: string) {
return this.branchesApplication.getBranch(Number(id));
}
@Post()
@ApiOperation({ summary: 'Create a new branch.' })
@ApiResponse({
status: 200,
description: 'The branch has been successfully created.',
})
@ApiResponse({ status: 404, description: 'The branch not found.' })
createBranch(@Body() createBranchDTO: CreateBranchDto) {
return this.branchesApplication.createBranch(createBranchDTO);
}
@Put(':id')
@ApiOperation({ summary: 'Edit the given branch.' })
@ApiResponse({
status: 200,
description: 'The branch has been successfully edited.',
})
@ApiResponse({ status: 404, description: 'The branch not found.' })
editBranch(@Param('id') id: string, @Body() editBranchDTO: EditBranchDto) {
return this.branchesApplication.editBranch(Number(id), editBranchDTO);
}
@Delete(':id')
@ApiOperation({ summary: 'Delete the given branch.' })
@ApiResponse({
status: 200,
description: 'The branch has been successfully deleted.',
})
@ApiResponse({ status: 404, description: 'The branch not found.' })
deleteBranch(@Param('id') id: string) {
return this.branchesApplication.deleteBranch(Number(id));
}
@Post('activate')
@ApiOperation({ summary: 'Activate the branches feature.' })
@ApiResponse({
status: 200,
description: 'The branches feature has been successfully activated.',
})
activateBranches() {
return this.branchesApplication.activateBranches();
}
@Put(':id/mark-as-primary')
@ApiOperation({ summary: 'Mark the given branch as primary.' })
@ApiResponse({
status: 200,
description: 'The branch has been successfully marked as primary.',
})
@ApiResponse({ status: 404, description: 'The branch not found.' })
markBranchAsPrimary(@Param('id') id: string) {
return this.branchesApplication.markBranchAsPrimary(Number(id));
}
}

View File

@@ -0,0 +1,77 @@
import { Module } from '@nestjs/common';
import { TenancyContext } from '../Tenancy/TenancyContext.service';
import { TenancyDatabaseModule } from '../Tenancy/TenancyDB/TenancyDB.module';
import { TransformerInjectable } from '../Transformer/TransformerInjectable.service';
import { BranchesController } from './Branches.controller';
import { CreateBranchService } from './commands/CreateBranch.service';
import { DeleteBranchService } from './commands/DeleteBranch.service';
import { EditBranchService } from './commands/EditBranch.service';
import { MarkBranchAsPrimaryService } from './commands/MarkBranchAsPrimary.service';
import { GetBranchService } from './queries/GetBranch.service';
import { GetBranchesService } from './queries/GetBranches.service';
import { ActivateBranches } from './commands/ActivateBranchesFeature.service';
import { BranchesApplication } from './BranchesApplication.service';
import { BranchesSettingsService } from './BranchesSettings';
import { BranchCommandValidator } from './commands/BranchCommandValidator.service';
import { BranchTransactionDTOTransformer } from './integrations/BranchTransactionDTOTransform';
import { ManualJournalBranchesDTOTransformer } from './integrations/ManualJournals/ManualJournalDTOTransformer.service';
import { BillBranchValidateSubscriber } from './subscribers/Validators/BillBranchSubscriber';
import { InventoryAdjustmentBranchValidateSubscriber } from './subscribers/Validators/InventoryAdjustmentBranchValidatorSubscriber';
import { ExpenseBranchValidateSubscriber } from './subscribers/Validators/ExpenseBranchSubscriber';
import { CreditNoteBranchValidateSubscriber } from './subscribers/Validators/CreditNoteBranchesSubscriber';
import { CreditNoteRefundBranchValidateSubscriber } from './subscribers/Validators/CreditNoteRefundBranchSubscriber';
import { ContactBranchValidateSubscriber } from './subscribers/Validators/ContactOpeningBalanceBranchSubscriber';
import { ManualJournalBranchValidateSubscriber } from './subscribers/Validators/ManualJournalBranchSubscriber';
import { SaleEstimateBranchValidateSubscriber } from './subscribers/Validators/SaleEstimateMultiBranchesSubscriber';
import { PaymentMadeBranchValidateSubscriber } from './subscribers/Validators/PaymentMadeBranchSubscriber';
import { PaymentReceiveBranchValidateSubscriber } from './subscribers/Validators/PaymentReceiveBranchSubscriber';
import { SaleReceiptBranchValidateSubscriber } from './subscribers/Validators/SaleReceiptBranchesSubscriber';
import { VendorCreditBranchValidateSubscriber } from './subscribers/Validators/VendorCreditBranchSubscriber';
import { ValidateBranchExistance } from './Integrations/ValidateBranchExistance';
import { ManualJournalBranchesValidator } from './Integrations/ManualJournals/ManualJournalsBranchesValidator';
import { CashflowTransactionsActivateBranches } from './integrations/Cashflow/CashflowActivateBranches';
import { ExpensesActivateBranches } from './integrations/Expense/ExpensesActivateBranches';
import { FeaturesModule } from '../Features/Features.module';
@Module({
imports: [TenancyDatabaseModule, FeaturesModule],
controllers: [BranchesController],
providers: [
CreateBranchService,
EditBranchService,
DeleteBranchService,
GetBranchService,
GetBranchesService,
MarkBranchAsPrimaryService,
ActivateBranches,
BranchesApplication,
BranchesSettingsService,
TenancyContext,
TransformerInjectable,
BranchCommandValidator,
BranchTransactionDTOTransformer,
ManualJournalBranchesDTOTransformer,
BillBranchValidateSubscriber,
CreditNoteBranchValidateSubscriber,
CreditNoteRefundBranchValidateSubscriber,
ContactBranchValidateSubscriber,
ExpenseBranchValidateSubscriber,
InventoryAdjustmentBranchValidateSubscriber,
ManualJournalBranchValidateSubscriber,
PaymentMadeBranchValidateSubscriber,
PaymentReceiveBranchValidateSubscriber,
SaleEstimateBranchValidateSubscriber,
SaleReceiptBranchValidateSubscriber,
VendorCreditBranchValidateSubscriber,
ValidateBranchExistance,
ManualJournalBranchesValidator,
CashflowTransactionsActivateBranches,
ExpensesActivateBranches
],
exports: [
BranchesSettingsService,
BranchTransactionDTOTransformer,
ManualJournalBranchesDTOTransformer,
],
})
export class BranchesModule {}

View File

@@ -0,0 +1,46 @@
import { Knex } from 'knex';
import { Branch } from './models/Branch.model';
export interface ICreateBranchDTO {
name: string;
code: string;
primary?: boolean;
}
export interface IEditBranchDTO {
code: string;
}
export interface IBranchCreatePayload {
// tenantId: number;
createBranchDTO: ICreateBranchDTO;
trx: Knex.Transaction;
}
export interface IBranchCreatedPayload {}
export interface IBranchEditPayload {}
export interface IBranchEditedPayload {}
export interface IBranchDeletePayload {}
export interface IBranchDeletedPayload {}
export interface IBranchesActivatePayload {
// tenantId: number;
trx: Knex.Transaction;
}
export interface IBranchesActivatedPayload {
// tenantId: number;
primaryBranch: Branch;
trx: Knex.Transaction;
}
export interface IBranchMarkAsPrimaryPayload {
// tenantId: number;
oldBranch: Branch;
trx: Knex.Transaction;
}
export interface IBranchMarkedAsPrimaryPayload {
// tenantId: number;
oldBranch: Branch;
markedBranch: Branch;
trx: Knex.Transaction;
}

View File

@@ -0,0 +1,108 @@
import { ICreateBranchDTO, IEditBranchDTO } from './Branches.types';
import { ActivateBranches } from './commands/ActivateBranchesFeature.service';
import {
CreateBranchService,
} from './commands/CreateBranch.service';
import {
DeleteBranchService,
} from './commands/DeleteBranch.service';
import { EditBranchService } from './commands/EditBranch.service';
import { GetBranchService } from './queries/GetBranch.service';
import { GetBranchesService } from './queries/GetBranches.service';
import { MarkBranchAsPrimaryService } from './commands/MarkBranchAsPrimary.service';
import { Branch } from './models/Branch.model';
import { Injectable } from '@nestjs/common';
import { CreateBranchDto, EditBranchDto } from './dtos/Branch.dto';
@Injectable()
export class BranchesApplication {
/**
* @param {CreateBranchService} createBranchService - Create branch service.
* @param {EditBranchService} editBranchService - Edit branch service.
* @param {DeleteBranchService} deleteBranchService - Delete branch service.
* @param {GetBranchService} getBranchService - Get branch service.
* @param {GetBranchesService} getBranchesService - Get branches service.
* @param {ActivateBranches} activateBranchesService - Activate branches service.
* @param {MarkBranchAsPrimaryService} markBranchAsPrimaryService - Mark branch as primary service.
*/
constructor(
private readonly createBranchService: CreateBranchService,
private readonly editBranchService: EditBranchService,
private readonly deleteBranchService: DeleteBranchService,
private readonly getBranchService: GetBranchService,
private readonly getBranchesService: GetBranchesService,
private readonly activateBranchesService: ActivateBranches,
private readonly markBranchAsPrimaryService: MarkBranchAsPrimaryService,
) {}
/**
* Retrieves branches list.
* @returns {Branch[]}
*/
public getBranches = (): Promise<Branch[]> => {
return this.getBranchesService.getBranches();
};
/**
* Retrieves the given branch details.
* @param {number} branchId - Branch id.
* @returns {Promise<IBranch>}
*/
public getBranch = (branchId: number): Promise<Branch> => {
return this.getBranchService.getBranch(branchId);
};
/**
* Creates a new branch.
* @param {number} tenantId -
* @param {ICreateBranchDTO} createBranchDTO
* @returns {Promise<IBranch>}
*/
public createBranch = (
createBranchDTO: CreateBranchDto,
): Promise<Branch> => {
return this.createBranchService.createBranch(createBranchDTO);
};
/**
* Edits the given branch.
* @param {number} branchId - Branch id.
* @param {IEditBranchDTO} editBranchDTO - Edit branch DTO.
* @returns {Promise<Branch>}
*/
public editBranch = (
branchId: number,
editBranchDTO: EditBranchDto,
): Promise<Branch> => {
return this.editBranchService.editBranch(branchId, editBranchDTO);
};
/**
* Deletes the given branch.
* @param {number} branchId - Branch id.
* @returns {Promise<void>}
*/
public deleteBranch = (branchId: number): Promise<void> => {
return this.deleteBranchService.deleteBranch(branchId);
};
/**
* Activates the given branches.
* @returns {Promise<void>}
*/
public activateBranches = (): Promise<void> => {
return this.activateBranchesService.activateBranches();
};
/**
* Marks the given branch as primary.
* @param {number} tenantId
* @param {number} branchId
* @returns {Promise<IBranch>}
*/
public markBranchAsPrimary = async (
branchId: number,
): Promise<Branch> => {
return this.markBranchAsPrimaryService.markAsPrimary(branchId);
};
}

View File

@@ -0,0 +1,30 @@
import { Inject, Injectable } from '@nestjs/common';
import { SettingsStore } from '../Settings/SettingsStore';
import { SETTINGS_PROVIDER } from '../Settings/Settings.types';
import { Features } from '@/common/types/Features';
@Injectable()
export class BranchesSettingsService {
constructor(
@Inject(SETTINGS_PROVIDER)
private readonly settingsStore: () => SettingsStore,
) {}
/**
* Marks multi-branches as activated.
*/
public markMultiBranchesAsActivated = async () => {
const settingsStore = await this.settingsStore();
settingsStore.set({ group: 'features', key: Features.BRANCHES, value: 1 });
};
/**
* Retrieves whether multi-branches is active.
*/
public isMultiBranchesActive = async () => {
const settingsStore = await this.settingsStore();
return settingsStore.get({ group: 'features', key: Features.BRANCHES });
};
}

View File

@@ -0,0 +1,30 @@
// import { Inject } from "typedi";
// import { ServiceError } from "exceptions";
// import HasTenancyService from "services/Tenancy/TenancyService";
// import { ERRORS } from "./constants";
// export class CURDBranch {
// @Inject()
// tenancy: HasTenancyService;
// /**
// *
// * @param branch
// */
// throwIfBranchNotFound = (branch) => {
// if (!branch) {
// throw new ServiceError(ERRORS.BRANCH_NOT_FOUND);
// }
// }
// getBranchOrThrowNotFound = async (tenantId: number, branchId: number) => {
// const { Branch } = this.tenancy.models(tenantId);
// const foundBranch = await Branch.query().findById(branchId);
// if (!foundBranch) {
// throw new ServiceError(ERRORS.BRANCH_NOT_FOUND);
// }
// return foundBranch;
// }
// }

View File

@@ -0,0 +1,50 @@
// import {
// CreditNoteActivateBranchesSubscriber,
// PaymentReceiveActivateBranchesSubscriber,
// SaleEstimatesActivateBranchesSubscriber,
// SaleInvoicesActivateBranchesSubscriber,
// PaymentMadeActivateBranchesSubscriber,
// SaleReceiptsActivateBranchesSubscriber,
// } from './Subscribers/Activate';
// import {
// BillBranchValidateSubscriber,
// VendorCreditBranchValidateSubscriber,
// PaymentMadeBranchValidateSubscriber,
// SaleEstimateBranchValidateSubscriber,
// CreditNoteBranchValidateSubscriber,
// ExpenseBranchValidateSubscriber,
// SaleReceiptBranchValidateSubscriber,
// ManualJournalBranchValidateSubscriber,
// PaymentReceiveBranchValidateSubscriber,
// CreditNoteRefundBranchValidateSubscriber,
// CashflowBranchDTOValidatorSubscriber,
// VendorCreditRefundBranchValidateSubscriber,
// InvoiceBranchValidateSubscriber,
// ContactBranchValidateSubscriber,
// InventoryAdjustmentBranchValidateSubscriber
// } from './Subscribers/Validators';
// export default () => [
// BillBranchValidateSubscriber,
// CreditNoteBranchValidateSubscriber,
// ExpenseBranchValidateSubscriber,
// PaymentMadeBranchValidateSubscriber,
// SaleReceiptBranchValidateSubscriber,
// VendorCreditBranchValidateSubscriber,
// SaleEstimateBranchValidateSubscriber,
// ManualJournalBranchValidateSubscriber,
// PaymentReceiveBranchValidateSubscriber,
// CreditNoteRefundBranchValidateSubscriber,
// VendorCreditRefundBranchValidateSubscriber,
// CreditNoteActivateBranchesSubscriber,
// PaymentReceiveActivateBranchesSubscriber,
// SaleEstimatesActivateBranchesSubscriber,
// SaleInvoicesActivateBranchesSubscriber,
// PaymentMadeActivateBranchesSubscriber,
// SaleReceiptsActivateBranchesSubscriber,
// CashflowBranchDTOValidatorSubscriber,
// InvoiceBranchValidateSubscriber,
// ContactBranchValidateSubscriber,
// InventoryAdjustmentBranchValidateSubscriber
// ];

View File

@@ -0,0 +1,78 @@
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { I18nService } from 'nestjs-i18n';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { ERRORS } from '../constants';
import {
IBranchesActivatedPayload,
IBranchesActivatePayload,
} from '../Branches.types';
import { CreateBranchService } from './CreateBranch.service';
import { BranchesSettingsService } from '../BranchesSettings';
import { ServiceError } from '@/modules/Items/ServiceError';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { Branch } from '../models/Branch.model';
import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class ActivateBranches {
constructor(
private readonly uow: UnitOfWork,
private readonly eventPublisher: EventEmitter2,
private readonly createBranch: CreateBranchService,
private readonly branchesSettings: BranchesSettingsService,
private readonly i18n: I18nService,
) {}
/**
* Throws service error if multi-branches feature is already activated.
*/
private throwIfMultiBranchesActivated = (isActivated: boolean) => {
if (isActivated) {
throw new ServiceError(ERRORS.MUTLI_BRANCHES_ALREADY_ACTIVATED);
}
};
/**
* Creates a new initial branch.
*/
private createInitialBranch = () => {
return this.createBranch.createBranch({
name: this.i18n.t('branches.head_branch'),
code: '10001',
primary: true,
});
};
/**
* Activate multi-branches feature.
* @returns {Promise<void>}
*/
public activateBranches = async (): Promise<void> => {
const isActivated = await this.branchesSettings.isMultiBranchesActive();
// Throw error if mutli-branches is already activated.
this.throwIfMultiBranchesActivated(isActivated);
// Activate multi-branches under unit-of-work environment.
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onBranchActivate` branch.
await this.eventPublisher.emitAsync(events.branch.onActivate, {
trx,
} as IBranchesActivatePayload);
// Create a new branch as primary branch.
const primaryBranch = await this.createInitialBranch();
// Mark the mutli-branches is activated.
await this.branchesSettings.markMultiBranchesAsActivated();
// Triggers `onBranchActivated` branch.
await this.eventPublisher.emitAsync(events.branch.onActivated, {
primaryBranch,
trx,
} as IBranchesActivatedPayload);
});
};
}

View File

@@ -0,0 +1,53 @@
import { Inject, Injectable } from '@nestjs/common';
import { ERRORS } from '../constants';
import { Branch } from '../models/Branch.model';
import { ServiceError } from '../../Items/ServiceError';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class BranchCommandValidator {
constructor(
@Inject(Branch.name)
private readonly branchModel: TenantModelProxy<typeof Branch>,
) {}
/**
* Validates the given branch whether is not only warehouse.
* @param {number} branchId
*/
public validateBranchNotOnlyWarehouse = async (branchId: number) => {
const warehouses = await this.branchModel()
.query()
.whereNot('id', branchId);
if (warehouses.length === 0) {
throw new ServiceError(ERRORS.COULD_NOT_DELETE_ONLY_BRANCH);
}
};
/**
* Validates the given branch whether is unique.
* @param {string} code - Branch code.
* @param {number} exceptBranchId - Branch id to except.
*/
public validateBranchCodeUnique = async (
code: string,
exceptBranchId?: number,
): Promise<void> => {
const branch = await this.branchModel()
.query()
.onBuild((query) => {
query.select(['id']);
query.where('code', code);
if (exceptBranchId) {
query.whereNot('id', exceptBranchId);
}
})
.first();
if (branch) {
throw new ServiceError(ERRORS.BRANCH_CODE_NOT_UNIQUE);
}
};
}

View File

@@ -0,0 +1,57 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import { IBranchCreatedPayload, IBranchCreatePayload } from '../Branches.types';
import { UnitOfWork } from '../../Tenancy/TenancyDB/UnitOfWork.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Branch } from '../models/Branch.model';
import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { CreateBranchDto } from '../dtos/Branch.dto';
@Injectable()
export class CreateBranchService {
/**
* @param {UnitOfWork} uow - Unit of Work for tenant database transactions.
* @param {EventEmitter2} eventPublisher - Event emitter for publishing branch creation events.
* @param {typeof Branch} branchModel - The Branch model class for database operations.
*/
constructor(
private readonly uow: UnitOfWork,
private readonly eventPublisher: EventEmitter2,
@Inject(Branch.name)
private readonly branchModel: TenantModelProxy<typeof Branch>,
) {}
/**
* Creates a new branch.
* @param {CreateBranchDto} createBranchDTO
* @returns {Promise<Branch>}
*/
public createBranch = async (
createBranchDTO: CreateBranchDto,
): Promise<Branch> => {
// Creates a new branch under unit-of-work.
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onBranchCreate` event.
await this.eventPublisher.emitAsync(events.warehouse.onEdit, {
createBranchDTO,
trx,
} as IBranchCreatePayload);
const branch = await this.branchModel()
.query()
.insertAndFetch({
...createBranchDTO,
});
// Triggers `onBranchCreated` event.
await this.eventPublisher.emitAsync(events.warehouse.onEdited, {
createBranchDTO,
branch,
trx,
} as IBranchCreatedPayload);
return branch;
});
};
}

View File

@@ -0,0 +1,66 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import { IBranchDeletedPayload, IBranchDeletePayload } from '../Branches.types';
import { BranchCommandValidator } from './BranchCommandValidator.service';
import { ERRORS } from '../constants';
import { Branch } from '../models/Branch.model';
import { UnitOfWork } from '../../Tenancy/TenancyDB/UnitOfWork.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class DeleteBranchService {
constructor(
@Inject(Branch.name)
private readonly branchModel: TenantModelProxy<typeof Branch>,
private readonly uow: UnitOfWork,
private readonly eventPublisher: EventEmitter2,
private readonly validator: BranchCommandValidator,
) {}
/**
* Validates the branch deleting.
* @param {number} branchId
* @returns {Promise<void>}
*/
private authorize = async (branchId: number): Promise<void> => {
await this.validator.validateBranchNotOnlyWarehouse(branchId);
};
/**
* Deletes branch.
* @param {number} branchId
* @returns {Promise<void>}
*/
public deleteBranch = async (branchId: number): Promise<void> => {
// Retrieves the old branch or throw not found service error.
const oldBranch = await this.branchModel()
.query()
.findById(branchId)
.throwIfNotFound();
// .queryAndThrowIfHasRelations({
// type: ERRORS.BRANCH_HAS_ASSOCIATED_TRANSACTIONS,
// });
// Authorize the branch before deleting.
await this.authorize(branchId);
// Deletes branch under unit-of-work.
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onBranchCreate` event.
await this.eventPublisher.emitAsync(events.warehouse.onEdit, {
oldBranch,
trx,
} as IBranchDeletePayload);
await this.branchModel().query().findById(branchId).delete();
// Triggers `onBranchCreate` event.
await this.eventPublisher.emitAsync(events.warehouse.onEdited, {
oldBranch,
trx,
} as IBranchDeletedPayload);
});
};
}

View File

@@ -0,0 +1,60 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import { IBranchEditedPayload, IBranchEditPayload } from '../Branches.types';
import { Branch } from '../models/Branch.model';
import { UnitOfWork } from '../../Tenancy/TenancyDB/UnitOfWork.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { EditBranchDto } from '../dtos/Branch.dto';
@Injectable()
export class EditBranchService {
constructor(
@Inject(Branch.name)
private readonly branchModel: TenantModelProxy<typeof Branch>,
private readonly uow: UnitOfWork,
private readonly eventPublisher: EventEmitter2,
) {}
/**
* Edits branch.
* @param {number} branchId - Branch id.
* @param {IEditBranchDTO} editBranchDTO - Edit branch data.
*/
public editBranch = async (
branchId: number,
editBranchDTO: EditBranchDto,
) => {
// Retrieves the old branch or throw not found service error.
const oldBranch = await this.branchModel()
.query()
.findById(branchId)
.throwIfNotFound();
// Deletes branch under unit-of-work.
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onBranchEdit` event.
await this.eventPublisher.emitAsync(events.warehouse.onEdit, {
oldBranch,
trx,
} as IBranchEditPayload);
// Edits the branch on the storage.
const branch = await this.branchModel()
.query()
.patchAndFetchById(branchId, {
...editBranchDTO,
});
// Triggers `onBranchEdited` event.
await this.eventPublisher.emitAsync(events.warehouse.onEdited, {
oldBranch,
branch,
trx,
} as IBranchEditedPayload);
return branch;
});
};
}

View File

@@ -0,0 +1,63 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import {
IBranchMarkAsPrimaryPayload,
IBranchMarkedAsPrimaryPayload,
} from '../Branches.types';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { UnitOfWork } from '../../Tenancy/TenancyDB/UnitOfWork.service';
import { Branch } from '../models/Branch.model';
import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class MarkBranchAsPrimaryService {
constructor(
private readonly uow: UnitOfWork,
private readonly eventPublisher: EventEmitter2,
@Inject(Branch.name)
private readonly branchModel: TenantModelProxy<typeof Branch>,
) {}
/**
* Marks the given branch as primary.
* @param {number} branchId
* @returns {Promise<IBranch>}
*/
public async markAsPrimary(branchId: number): Promise<Branch> {
// Retrieves the old branch or throw not found service error.
const oldBranch = await this.branchModel()
.query()
.findById(branchId)
.throwIfNotFound();
// Updates the branches under unit-of-work environment.
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onBranchMarkPrimary` event.
await this.eventPublisher.emitAsync(events.branch.onMarkPrimary, {
oldBranch,
trx,
} as IBranchMarkAsPrimaryPayload);
// Updates all branches as not primary.
await this.branchModel().query(trx).update({ primary: false });
// Updates the given branch as primary.
const markedBranch = await this.branchModel()
.query(trx)
.patchAndFetchById(branchId, {
primary: true,
});
// Triggers `onBranchMarkedPrimary` event.
await this.eventPublisher.emitAsync(events.branch.onMarkedPrimary, {
markedBranch,
oldBranch,
trx,
} as IBranchMarkedAsPrimaryPayload);
return markedBranch;
});
}
}

View File

@@ -0,0 +1,7 @@
export const ERRORS = {
BRANCH_NOT_FOUND: 'BRANCH_NOT_FOUND',
MUTLI_BRANCHES_ALREADY_ACTIVATED: 'MUTLI_BRANCHES_ALREADY_ACTIVATED',
COULD_NOT_DELETE_ONLY_BRANCH: 'COULD_NOT_DELETE_ONLY_BRANCH',
BRANCH_CODE_NOT_UNIQUE: 'BRANCH_CODE_NOT_UNIQUE',
BRANCH_HAS_ASSOCIATED_TRANSACTIONS: 'BRANCH_HAS_ASSOCIATED_TRANSACTIONS'
};

View File

@@ -0,0 +1,89 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import {
IsBoolean,
IsEmail,
IsNotEmpty,
IsOptional,
IsString,
IsUrl,
} from 'class-validator';
class CommandBranchDto {
@ApiProperty({
description: 'Branch name',
example: 'Main Branch',
})
@IsNotEmpty()
@IsString()
name: string;
@ApiPropertyOptional({
description: 'Whether this is the primary branch',
example: true,
default: false,
})
@IsOptional()
@IsBoolean()
primary?: boolean;
@ApiPropertyOptional({
description: 'Branch code',
example: 'BR001',
})
@IsOptional()
@IsString()
code?: string;
@ApiPropertyOptional({
description: 'Branch address',
example: '123 Main Street',
})
@IsOptional()
@IsString()
address?: string;
@ApiPropertyOptional({
description: 'Branch city',
example: 'New York',
})
@IsOptional()
@IsString()
city?: string;
@ApiPropertyOptional({
description: 'Branch country',
example: 'USA',
})
@IsOptional()
@IsString()
country?: string;
@ApiPropertyOptional({
description: 'Branch phone number',
example: '+1-555-123-4567',
})
@IsOptional()
@IsString()
phone_number?: string;
@ApiPropertyOptional({
description: 'Branch email',
example: 'branch@example.com',
})
@IsOptional()
@IsEmail()
@IsString()
email?: string;
@ApiPropertyOptional({
description: 'Branch website',
example: 'https://www.example.com/branch',
})
@IsOptional()
@IsUrl()
@IsString()
website?: string;
}
export class CreateBranchDto extends CommandBranchDto {}
export class EditBranchDto extends CommandBranchDto {}

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'
}

View File

@@ -0,0 +1,192 @@
// import { Model, mixin } from 'objection';
// import TenantModel from 'models/TenantModel';
// import BranchMetadata from './Branch.settings';
// import ModelSetting from './ModelSetting';
import { BaseModel } from '@/models/Model';
export class Branch extends BaseModel{
name!: string;
code!: string;
address!: string;
city!: string;
country!: string;
phoneNumber!: string;
email!: string;
website!: string;
primary!: boolean;
/**
* Table name.
*/
static get tableName() {
return 'branches';
}
/**
* Timestamps columns.
*/
get timestamps() {
return ['created_at', 'updated_at'];
}
/**
* Model modifiers.
*/
static get modifiers() {
return {
/**
* Filters accounts by the given ids.
* @param {Query} query
* @param {number[]} accountsIds
*/
isPrimary(query) {
query.where('primary', true);
},
};
}
/**
* Relationship mapping.
*/
// static get relationMappings() {
// const SaleInvoice = require('models/SaleInvoice');
// const SaleEstimate = require('models/SaleEstimate');
// const SaleReceipt = require('models/SaleReceipt');
// const Bill = require('models/Bill');
// const PaymentReceive = require('models/PaymentReceive');
// const PaymentMade = require('models/BillPayment');
// const VendorCredit = require('models/VendorCredit');
// const CreditNote = require('models/CreditNote');
// const AccountTransaction = require('models/AccountTransaction');
// const InventoryTransaction = require('models/InventoryTransaction');
// return {
// /**
// * Branch may belongs to associated sale invoices.
// */
// invoices: {
// relation: Model.HasManyRelation,
// modelClass: SaleInvoice.default,
// join: {
// from: 'branches.id',
// to: 'sales_invoices.branchId',
// },
// },
// /**
// * Branch may belongs to associated sale estimates.
// */
// estimates: {
// relation: Model.HasManyRelation,
// modelClass: SaleEstimate.default,
// join: {
// from: 'branches.id',
// to: 'sales_estimates.branchId',
// },
// },
// /**
// * Branch may belongs to associated sale receipts.
// */
// receipts: {
// relation: Model.HasManyRelation,
// modelClass: SaleReceipt.default,
// join: {
// from: 'branches.id',
// to: 'sales_receipts.branchId',
// },
// },
// /**
// * Branch may belongs to associated payment receives.
// */
// paymentReceives: {
// relation: Model.HasManyRelation,
// modelClass: PaymentReceive.default,
// join: {
// from: 'branches.id',
// to: 'payment_receives.branchId',
// },
// },
// /**
// * Branch may belongs to associated bills.
// */
// bills: {
// relation: Model.HasManyRelation,
// modelClass: Bill.default,
// join: {
// from: 'branches.id',
// to: 'bills.branchId',
// },
// },
// /**
// * Branch may belongs to associated payment mades.
// */
// paymentMades: {
// relation: Model.HasManyRelation,
// modelClass: PaymentMade.default,
// join: {
// from: 'branches.id',
// to: 'bills_payments.branchId',
// },
// },
// /**
// * Branch may belongs to associated credit notes.
// */
// creditNotes: {
// relation: Model.HasManyRelation,
// modelClass: CreditNote.default,
// join: {
// from: 'branches.id',
// to: 'credit_notes.branchId',
// },
// },
// /**
// * Branch may belongs to associated to vendor credits.
// */
// vendorCredit: {
// relation: Model.HasManyRelation,
// modelClass: VendorCredit.default,
// join: {
// from: 'branches.id',
// to: 'vendor_credits.branchId',
// },
// },
// /**
// * Branch may belongs to associated to accounts transactions.
// */
// accountsTransactions: {
// relation: Model.HasManyRelation,
// modelClass: AccountTransaction.default,
// join: {
// from: 'branches.id',
// to: 'accounts_transactions.branchId',
// },
// },
// /**
// * Branch may belongs to associated to inventory transactions.
// */
// inventoryTransactions: {
// relation: Model.HasManyRelation,
// modelClass: InventoryTransaction.default,
// join: {
// from: 'branches.id',
// to: 'inventory_transactions.branchId',
// },
// },
// };
// }
/**
* Model settings.
*/
// static get meta() {
// return BranchMetadata;
// }
}

View File

@@ -0,0 +1,26 @@
import { Inject } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import { Branch } from '../models/Branch.model';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class GetBranchService {
constructor(
@Inject(Branch.name)
private readonly branch: TenantModelProxy<typeof Branch>,
) {}
/**
* Retrieves the given branch details.
* @param {number} branchId
* @returns {Promise<IBranch>}
*/
public getBranch = async (branchId: number): Promise<Branch> => {
const branch = await this.branch()
.query()
.findById(branchId)
.throwIfNotFound();
return branch;
};
}

View File

@@ -0,0 +1,21 @@
import { Inject, Injectable } from '@nestjs/common';
import { Branch } from '../models/Branch.model';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class GetBranchesService {
constructor(
@Inject(Branch.name)
private readonly branch: TenantModelProxy<typeof Branch>,
) {}
/**
* Retrieves branches list.
* @returns
*/
public getBranches = async () => {
const branches = await this.branch().query().orderBy('name', 'DESC');
return branches;
};
}

View File

@@ -0,0 +1,29 @@
import { OnEvent } from '@nestjs/event-emitter';
import { Injectable } from '@nestjs/common';
import { CashflowTransactionsActivateBranches } from '../../Integrations/Cashflow/CashflowActivateBranches';
import { IBranchesActivatedPayload } from '../../Branches.types';
import { events } from '@/common/events/events';
@Injectable()
export class CreditNoteActivateBranchesSubscriber {
constructor(
private readonly cashflowActivateBranches: CashflowTransactionsActivateBranches,
) {}
/**
* Updates accounts transactions with the primary branch once
* the multi-branches is activated.
* @param {IBranchesActivatedPayload}
*/
@OnEvent(events.branch.onActivated)
async updateCashflowWithBranchOnActivated({
primaryBranch,
trx,
}: IBranchesActivatedPayload) {
await this.cashflowActivateBranches.updateCashflowTransactionsWithBranch(
primaryBranch.id,
trx
);
};
}

View File

@@ -0,0 +1,28 @@
import { events } from '@/common/events/events';
import { OnEvent } from '@nestjs/event-emitter';
import { IBranchesActivatedPayload } from '../../Branches.types';
import { Injectable } from '@nestjs/common';
import { CreditNoteActivateBranches } from '../../integrations/Sales/CreditNoteBranchesActivate';
@Injectable()
export class CreditNoteActivateBranchesSubscriber {
constructor(
private readonly creditNotesActivateBranches: CreditNoteActivateBranches,
) {}
/**
* Updates accounts transactions with the primary branch once
* the multi-branches is activated.
* @param {IBranchesActivatedPayload}
*/
@OnEvent(events.branch.onActivated)
async updateCreditNoteWithBranchOnActivated({
primaryBranch,
trx,
}: IBranchesActivatedPayload) {
await this.creditNotesActivateBranches.updateCreditsWithBranch(
primaryBranch.id,
trx
);
};
}

View File

@@ -0,0 +1,28 @@
import { IBranchesActivatedPayload } from '../../Branches.types';
import { events } from '@/common/events/events';
import { Injectable } from '@nestjs/common';
import { ExpensesActivateBranches } from '../../Integrations/Expense/ExpensesActivateBranches';
import { OnEvent } from '@nestjs/event-emitter';
@Injectable()
export class ExpenseActivateBranchesSubscriber {
constructor(
private readonly expensesActivateBranches: ExpensesActivateBranches,
) {}
/**
* Updates accounts transactions with the primary branch once
* the multi-branches is activated.
* @param {IBranchesActivatedPayload}
*/
@OnEvent(events.branch.onActivated)
async updateExpensesWithBranchOnActivated({
primaryBranch,
trx,
}: IBranchesActivatedPayload) {
await this.expensesActivateBranches.updateExpensesWithBranch(
primaryBranch.id,
trx,
);
}
}

View File

@@ -0,0 +1,28 @@
import { BillPaymentsActivateBranches } from '../../Integrations/Purchases/PaymentMadeBranchesActivate';
import { OnEvent } from '@nestjs/event-emitter';
import { Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
import { IBranchesActivatedPayload } from '../../Branches.types';
@Injectable()
export class PaymentMadeActivateBranchesSubscriber {
constructor(
private readonly paymentsActivateBranches: BillPaymentsActivateBranches,
) {}
/**
* Updates accounts transactions with the primary branch once
* the multi-branches is activated.
* @param {IBranchesActivatedPayload}
*/
@OnEvent(events.branch.onActivated)
async updatePaymentsWithBranchOnActivated({
primaryBranch,
trx,
}: IBranchesActivatedPayload) {
await this.paymentsActivateBranches.updateBillPaymentsWithBranch(
primaryBranch.id,
trx,
);
}
}

View File

@@ -0,0 +1,28 @@
import { events } from '@/common/events/events';
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { IBranchesActivatedPayload } from '../../Branches.types';
import { PaymentReceiveActivateBranches } from '../../integrations/Sales/PaymentReceiveBranchesActivate';
@Injectable()
export class PaymentReceiveActivateBranchesSubscriber {
constructor(
private readonly paymentsActivateBranches: PaymentReceiveActivateBranches,
) {}
/**
* Updates accounts transactions with the primary branch once
* the multi-branches is activated.
* @param {IBranchesActivatedPayload}
*/
@OnEvent(events.branch.onActivated)
async updateCreditNoteWithBranchOnActivated({
primaryBranch,
trx,
}: IBranchesActivatedPayload) {
await this.paymentsActivateBranches.updatePaymentsWithBranch(
primaryBranch.id,
trx
);
};
}

View File

@@ -0,0 +1,28 @@
import { OnEvent } from '@nestjs/event-emitter';
import { Injectable } from '@nestjs/common';
import { IBranchesActivatedPayload } from '../../Branches.types';
import { events } from '@/common/events/events';
import { SaleEstimateActivateBranches } from '../../integrations/Sales/SaleEstimatesBranchesActivate';
@Injectable()
export class SaleEstimatesActivateBranchesSubscriber {
constructor(
private readonly estimatesActivateBranches: SaleEstimateActivateBranches,
) {}
/**
* Updates accounts transactions with the primary branch once
* the multi-branches is activated.
* @param {IBranchesActivatedPayload}
*/
@OnEvent(events.branch.onActivated)
async updateEstimatesWithBranchOnActivated({
primaryBranch,
trx,
}: IBranchesActivatedPayload) {
await this.estimatesActivateBranches.updateEstimatesWithBranch(
primaryBranch.id,
trx
);
};
}

View File

@@ -0,0 +1,28 @@
import { events } from '@/common/events/events';
import { SaleInvoiceActivateBranches } from '../../Integrations/Sales/SaleInvoiceBranchesActivate';
import { OnEvent } from '@nestjs/event-emitter';
import { IBranchesActivatedPayload } from '../../Branches.types';
import { Injectable } from '@nestjs/common';
@Injectable()
export class SaleInvoicesActivateBranchesSubscriber {
constructor(
private readonly invoicesActivateBranches: SaleInvoiceActivateBranches,
) {}
/**
* Updates accounts transactions with the primary branch once
* the multi-branches is activated.
* @param {IBranchesActivatedPayload}
*/
@OnEvent(events.branch.onActivated)
async updateInvoicesWithBranchOnActivated({
primaryBranch,
trx,
}: IBranchesActivatedPayload) {
await this.invoicesActivateBranches.updateInvoicesWithBranch(
primaryBranch.id,
trx
);
};
}

View File

@@ -0,0 +1,28 @@
import { OnEvent } from '@nestjs/event-emitter';
import { Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
import { IBranchesActivatedPayload } from '../../Branches.types';
import { SaleReceiptActivateBranches } from '../../integrations/Sales/SaleReceiptBranchesActivate';
@Injectable()
export class SaleReceiptsActivateBranchesSubscriber {
constructor(
private readonly receiptsActivateBranches: SaleReceiptActivateBranches,
) {}
/**
* Updates accounts transactions with the primary branch once
* the multi-branches is activated.
* @param {IBranchesActivatedPayload}
*/
@OnEvent(events.branch.onActivated)
async updateReceiptsWithBranchOnActivated({
primaryBranch,
trx,
}: IBranchesActivatedPayload) {
await this.receiptsActivateBranches.updateReceiptsWithBranch(
primaryBranch.id,
trx,
);
}
}

View File

@@ -0,0 +1,39 @@
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
import {
IBillCreatingPayload,
IBillEditingPayload,
} from '@/modules/Bills/Bills.types';
@Injectable()
export class BillBranchValidateSubscriber {
constructor(
private readonly validateBranchExistance: ValidateBranchExistance,
) {}
/**
* Validate branch existance on bill creating.
* @param {IBillCreatingPayload} payload
*/
@OnEvent(events.bill.onCreating)
async validateBranchExistanceOnBillCreating({
billDTO,
}: IBillCreatingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
billDTO.branchId,
);
}
/**
* Validate branch existance once bill editing.
* @param {IBillEditingPayload} payload
*/
@OnEvent(events.bill.onEditing)
async validateBranchExistanceOnBillEditing({ billDTO }: IBillEditingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
billDTO.branchId,
);
}
}

View File

@@ -0,0 +1,25 @@
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
import { ICommandCashflowCreatingPayload } from '@/modules/BankingTransactions/types/BankingTransactions.types';
@Injectable()
export class CashflowBranchDTOValidatorSubscriber {
constructor(
private readonly validateBranchExistance: ValidateBranchExistance,
) {}
/**
* Validate branch existance once cashflow transaction creating.
* @param {ICommandCashflowCreatingPayload} payload
*/
@OnEvent(events.cashflow.onTransactionCreating)
async validateBranchExistanceOnCashflowTransactionCreating({
newTransactionDTO,
}: ICommandCashflowCreatingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
newTransactionDTO.branchId,
);
}
}

View File

@@ -0,0 +1,81 @@
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
import {
ICustomerOpeningBalanceEditingPayload,
ICustomerEventCreatingPayload,
} from '@/modules/Customers/types/Customers.types';
import {
IVendorEventCreatingPayload,
IVendorOpeningBalanceEditingPayload,
} from '@/modules/Vendors/types/Vendors.types';
@Injectable()
export class ContactBranchValidateSubscriber {
constructor(
private readonly validateBranchExistance: ValidateBranchExistance,
) {}
/**
* Validate branch existance on customer creating.
* @param {ICustomerEventCreatingPayload} payload
*/
@OnEvent(events.customers.onCreating)
async validateBranchExistanceOnCustomerCreating({
customerDTO,
}: ICustomerEventCreatingPayload) {
// Can't continue if the customer opening balance is zero.
if (!customerDTO.openingBalance) return;
await this.validateBranchExistance.validateTransactionBranchWhenActive(
customerDTO.openingBalanceBranchId,
);
}
/**
* Validate branch existance once customer opening balance editing.
* @param {ICustomerOpeningBalanceEditingPayload} payload
*/
@OnEvent(events.customers.onOpeningBalanceChanging)
async validateBranchExistanceOnCustomerOpeningBalanceEditing({
openingBalanceEditDTO,
}: ICustomerOpeningBalanceEditingPayload) {
if (!openingBalanceEditDTO.openingBalance) return;
await this.validateBranchExistance.validateTransactionBranchWhenActive(
openingBalanceEditDTO.openingBalanceBranchId,
);
}
/**
* Validates the branch existance on vendor creating.
* @param {IVendorEventCreatingPayload} payload
*/
@OnEvent(events.vendors.onCreating)
async validateBranchExistanceonVendorCreating({
vendorDTO,
}: IVendorEventCreatingPayload) {
// Can't continue if the customer opening balance is zero.
if (!vendorDTO.openingBalance) return;
await this.validateBranchExistance.validateTransactionBranchWhenActive(
vendorDTO.openingBalanceBranchId,
);
}
/**
* Validate branch existance once the vendor opening balance editing.
* @param {IVendorOpeningBalanceEditingPayload} payload
*/
@OnEvent(events.vendors.onOpeningBalanceChanging)
async validateBranchExistanceOnVendorOpeningBalanceEditing({
openingBalanceEditDTO,
}: IVendorOpeningBalanceEditingPayload) {
if (!openingBalanceEditDTO.openingBalance) return;
await this.validateBranchExistance.validateTransactionBranchWhenActive(
openingBalanceEditDTO.openingBalanceBranchId,
);
}
}

View File

@@ -0,0 +1,39 @@
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
import { ICreditNoteEditingPayload } from '@/modules/CreditNotes/types/CreditNotes.types';
import { ICreditNoteCreatingPayload } from '@/modules/CreditNotes/types/CreditNotes.types';
@Injectable()
export class CreditNoteBranchValidateSubscriber {
constructor(
private readonly validateBranchExistance: ValidateBranchExistance,
) {}
/**
* Validate branch existance on credit note creating.
* @param {ICreditNoteCreatingPayload} payload
*/
@OnEvent(events.creditNote.onCreating)
async validateBranchExistanceOnCreditCreating({
creditNoteDTO,
}: ICreditNoteCreatingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
creditNoteDTO.branchId,
);
}
/**
* Validate branch existance once credit note editing.
* @param {ICreditNoteEditingPayload} payload
*/
@OnEvent(events.creditNote.onEditing)
async validateBranchExistanceOnCreditEditing({
creditNoteEditDTO,
}: ICreditNoteEditingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
creditNoteEditDTO.branchId,
);
}
}

View File

@@ -0,0 +1,25 @@
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
import { events } from '@/common/events/events';
import { IRefundCreditNoteCreatingPayload } from '@/modules/CreditNoteRefunds/types/CreditNoteRefunds.types';
@Injectable()
export class CreditNoteRefundBranchValidateSubscriber {
constructor(
private readonly validateBranchExistance: ValidateBranchExistance
) {}
/**
* Validate branch existance on refund credit note creating.
* @param {IRefundCreditNoteCreatingPayload} payload
*/
@OnEvent(events.creditNote.onRefundCreating)
async validateBranchExistanceOnCreditRefundCreating({
newCreditNoteDTO,
}: IRefundCreditNoteCreatingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
newCreditNoteDTO.branchId
);
}
}

View File

@@ -0,0 +1,40 @@
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
import {
IExpenseCreatingPayload,
IExpenseEventEditingPayload,
} from '@/modules/Expenses/Expenses.types';
@Injectable()
export class ExpenseBranchValidateSubscriber {
constructor(
private readonly validateBranchExistance: ValidateBranchExistance,
) {}
/**
* Validate branch existance once expense transaction creating.
* @param {IExpenseCreatingPayload} payload
*/
@OnEvent(events.expenses.onCreating)
async validateBranchExistanceOnExpenseCreating({
expenseDTO,
}: IExpenseCreatingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
expenseDTO.branchId,
);
}
/**
* Validate branch existance once expense transaction editing.
* @param {IExpenseEventEditingPayload} payload
*/
@OnEvent(events.expenses.onEditing)
async validateBranchExistanceOnExpenseEditing({
expenseDTO,
}: IExpenseEventEditingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
expenseDTO.branchId,
);
}
}

View File

@@ -0,0 +1,25 @@
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
import { IInventoryAdjustmentCreatingPayload } from '@/modules/InventoryAdjutments/types/InventoryAdjustments.types';
@Injectable()
export class InventoryAdjustmentBranchValidateSubscriber {
constructor(
private readonly validateBranchExistance: ValidateBranchExistance,
) {}
/**
* Validate branch existance on inventory adjustment creating.
* @param {IInventoryAdjustmentCreatingPayload} payload
*/
@OnEvent(events.inventoryAdjustment.onQuickCreating)
async validateBranchExistanceOnInventoryCreating({
quickAdjustmentDTO,
}: IInventoryAdjustmentCreatingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
quickAdjustmentDTO.branchId,
);
}
}

View File

@@ -0,0 +1,41 @@
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
import {
ISaleInvoiceCreatingPaylaod,
ISaleInvoiceEditingPayload,
} from '@/modules/SaleInvoices/SaleInvoice.types';
@Injectable()
export class InvoiceBranchValidateSubscriber {
constructor(
private readonly validateBranchExistance: ValidateBranchExistance,
) {}
/**
* Validate branch existance on invoice creating.
* @param {ISaleInvoiceCreatingPayload} payload
*/
@OnEvent(events.saleInvoice.onCreating)
async validateBranchExistanceOnInvoiceCreating({
saleInvoiceDTO,
}: ISaleInvoiceCreatingPaylaod) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
saleInvoiceDTO.branchId,
);
}
/**
* Validate branch existance once invoice editing.
* @param {ISaleInvoiceEditingPayload} payload
*/
@OnEvent(events.saleInvoice.onEditing)
async validateBranchExistanceOnInvoiceEditing({
saleInvoiceDTO,
}: ISaleInvoiceEditingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
saleInvoiceDTO.branchId,
);
}
}

View File

@@ -0,0 +1,60 @@
import {
IManualJournalCreatingPayload,
IManualJournalEditingPayload,
} from '@/modules/ManualJournals/types/ManualJournals.types';
import { ManualJournalBranchesValidator } from '../../Integrations/ManualJournals/ManualJournalsBranchesValidator';
import { OnEvent } from '@nestjs/event-emitter';
import { Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
import { Features } from '@/common/types/Features';
import { FeaturesManager } from '../../../Features/FeaturesManager';
@Injectable()
export class ManualJournalBranchValidateSubscriber {
constructor(
private readonly validateManualJournalBranch: ManualJournalBranchesValidator,
private readonly featuresManager: FeaturesManager,
) {}
/**
* Validate branch existance on estimate creating.
* @param {IManualJournalCreatingPayload} payload
*/
@OnEvent(events.manualJournals.onCreating)
async validateBranchExistanceOnBillCreating({
manualJournalDTO,
}: IManualJournalCreatingPayload) {
// Detarmines whether the multi-branches is accessible by tenant.
const isAccessible = await this.featuresManager.accessible(
Features.BRANCHES
);
// Can't continue if the multi-branches feature is inactive.
if (!isAccessible) return;
// Validates the entries whether have branch id.
await this.validateManualJournalBranch.validateEntriesHasBranchId(
manualJournalDTO
);
};
/**
* Validate branch existance once estimate editing.
* @param {ISaleEstimateEditingPayload} payload
*/
@OnEvent(events.manualJournals.onEditing)
async validateBranchExistanceOnBillEditing({
manualJournalDTO,
}: IManualJournalEditingPayload) {
// Detarmines whether the multi-branches is accessible by tenant.
const isAccessible = await this.featuresManager.accessible(
Features.BRANCHES
);
// Can't continue if the multi-branches feature is inactive.
if (!isAccessible) return;
await this.validateManualJournalBranch.validateEntriesHasBranchId(
manualJournalDTO
);
};
}

View File

@@ -0,0 +1,41 @@
import { OnEvent } from '@nestjs/event-emitter';
import { Injectable } from '@nestjs/common';
import {
IBillPaymentCreatingPayload,
IBillPaymentEditingPayload,
} from '@/modules/BillPayments/types/BillPayments.types';
import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
import { events } from '@/common/events/events';
@Injectable()
export class PaymentMadeBranchValidateSubscriber {
constructor(
private readonly validateBranchExistance: ValidateBranchExistance,
) {}
/**
* Validate branch existance on estimate creating.
* @param {ISaleEstimateCreatedPayload} payload
*/
@OnEvent(events.billPayment.onCreating)
async validateBranchExistanceOnPaymentCreating({
billPaymentDTO,
}: IBillPaymentCreatingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
billPaymentDTO.branchId,
);
}
/**
* Validate branch existance once estimate editing.
* @param {ISaleEstimateEditingPayload} payload
*/
@OnEvent(events.billPayment.onEditing)
async validateBranchExistanceOnPaymentEditing({
billPaymentDTO,
}: IBillPaymentEditingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
billPaymentDTO.branchId,
);
}
}

View File

@@ -0,0 +1,41 @@
import {
IPaymentReceivedCreatingPayload,
IPaymentReceivedEditingPayload,
} from '@/modules/PaymentReceived/types/PaymentReceived.types';
import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
import { OnEvent } from '@nestjs/event-emitter';
import { Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
@Injectable()
export class PaymentReceiveBranchValidateSubscriber {
constructor(
private readonly validateBranchExistance: ValidateBranchExistance,
) {}
/**
* Validate branch existance on estimate creating.
* @param {IPaymentReceivedCreatingPayload} payload
*/
@OnEvent(events.paymentReceive.onCreating)
async validateBranchExistanceOnPaymentCreating({
paymentReceiveDTO,
}: IPaymentReceivedCreatingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
paymentReceiveDTO.branchId,
);
}
/**
* Validate branch existance once estimate editing.
* @param {IPaymentReceivedEditingPayload} payload
*/
@OnEvent(events.paymentReceive.onEditing)
async validateBranchExistanceOnPaymentEditing({
paymentReceiveDTO,
}: IPaymentReceivedEditingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
paymentReceiveDTO.branchId,
);
}
}

View File

@@ -0,0 +1,41 @@
import { OnEvent } from '@nestjs/event-emitter';
import { Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
import {
ISaleEstimateCreatingPayload,
ISaleEstimateEditingPayload,
} from '@/modules/SaleEstimates/types/SaleEstimates.types';
import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
@Injectable()
export class SaleEstimateBranchValidateSubscriber {
constructor(
private readonly validateBranchExistance: ValidateBranchExistance,
) {}
/**
* Validate branch existance on estimate creating.
* @param {ISaleEstimateCreatedPayload} payload
*/
@OnEvent(events.saleEstimate.onCreating)
async validateBranchExistanceOnEstimateCreating({
estimateDTO,
}: ISaleEstimateCreatingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
estimateDTO.branchId,
);
}
/**
* Validate branch existance once estimate editing.
* @param {ISaleEstimateEditingPayload} payload
*/
@OnEvent(events.saleEstimate.onEditing)
async validateBranchExistanceOnEstimateEditing({
estimateDTO,
}: ISaleEstimateEditingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
estimateDTO.branchId,
);
}
}

View File

@@ -0,0 +1,41 @@
import {
ISaleReceiptCreatingPayload,
ISaleReceiptEditingPayload,
} from '@/modules/SaleReceipts/types/SaleReceipts.types';
import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
import { OnEvent } from '@nestjs/event-emitter';
import { Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
@Injectable()
export class SaleReceiptBranchValidateSubscriber {
constructor(
private readonly validateBranchExistance: ValidateBranchExistance,
) {}
/**
* Validate branch existance on estimate creating.
* @param {ISaleReceiptCreatingPayload} payload
*/
@OnEvent(events.saleReceipt.onCreating)
async validateBranchExistanceOnInvoiceCreating({
saleReceiptDTO,
}: ISaleReceiptCreatingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
saleReceiptDTO.branchId,
);
}
/**
* Validate branch existance once estimate editing.
* @param {ISaleReceiptEditingPayload} payload
*/
@OnEvent(events.saleReceipt.onEditing)
async validateBranchExistanceOnInvoiceEditing({
saleReceiptDTO,
}: ISaleReceiptEditingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
saleReceiptDTO.branchId,
);
}
}

View File

@@ -0,0 +1,41 @@
import {
IVendorCreditCreatingPayload,
IVendorCreditEditingPayload,
} from '@/modules/VendorCredit/types/VendorCredit.types';
import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
import { OnEvent } from '@nestjs/event-emitter';
import { Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
@Injectable()
export class VendorCreditBranchValidateSubscriber {
constructor(
private readonly validateBranchExistance: ValidateBranchExistance,
) {}
/**
* Validate branch existance on estimate creating.
* @param {ISaleEstimateCreatedPayload} payload
*/
@OnEvent(events.vendorCredit.onCreating)
async validateBranchExistanceOnCreditCreating({
vendorCreditCreateDTO,
}: IVendorCreditCreatingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
vendorCreditCreateDTO.branchId,
);
}
/**
* Validate branch existance once estimate editing.
* @param {ISaleEstimateEditingPayload} payload
*/
@OnEvent(events.vendorCredit.onEditing)
async validateBranchExistanceOnCreditEditing({
vendorCreditDTO,
}: IVendorCreditEditingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
vendorCreditDTO.branchId,
);
}
}

View File

@@ -0,0 +1,25 @@
import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
import { OnEvent } from '@nestjs/event-emitter';
import { Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
import { IRefundVendorCreditCreatingPayload } from '@/modules/VendorCreditsRefund/types/VendorCreditRefund.types';
@Injectable()
export class VendorCreditRefundBranchValidateSubscriber {
constructor(
private readonly validateBranchExistance: ValidateBranchExistance,
) {}
/**
* Validate branch existance on refund credit note creating.
* @param {IRefundVendorCreditCreatingPayload} payload
*/
@OnEvent(events.vendorCredit.onRefundCreating)
async validateBranchExistanceOnCreditRefundCreating({
refundVendorCreditDTO,
}: IRefundVendorCreditCreatingPayload) {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
refundVendorCreditDTO.branchId,
);
}
}