refactor: branches and warehouses to nestjs

This commit is contained in:
Ahmed Bouhuolia
2024-12-21 00:10:09 +02:00
parent dc52f784b6
commit cb8fd68d46
126 changed files with 5419 additions and 0 deletions

View File

@@ -36,6 +36,7 @@ import { ExpensesModule } from '../Expenses/Expenses.module';
import { ItemCategoryModule } from '../ItemCategories/ItemCategory.module';
import { TaxRatesModule } from '../TaxRates/TaxRate.module';
import { PdfTemplatesModule } from '../PdfTemplate/PdfTemplates.module';
import { BranchesModule } from '../Branches/Branches.module';
@Module({
imports: [
@@ -95,6 +96,7 @@ import { PdfTemplatesModule } from '../PdfTemplate/PdfTemplates.module';
ExpensesModule,
TaxRatesModule,
PdfTemplatesModule,
BranchesModule,
],
controllers: [AppController],
providers: [

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,51 @@
import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
} from '@nestjs/common';
import { BranchesApplication } from './BranchesApplication.service';
import { ICreateBranchDTO, IEditBranchDTO } from './Branches.types';
@Controller('branches')
export class BranchesController {
constructor(private readonly branchesApplication: BranchesApplication) {}
@Get()
getBranches() {
// return this.branchesApplication.getBranches();
}
@Get(':id')
getBranch(@Param('id') id: string) {
return this.branchesApplication.getBranch(Number(id));
}
@Post()
createBranch(@Body() createBranchDTO: ICreateBranchDTO) {
return this.branchesApplication.createBranch(createBranchDTO);
}
@Put(':id')
editBranch(@Param('id') id: string, @Body() editBranchDTO: IEditBranchDTO) {
return this.branchesApplication.editBranch(Number(id), editBranchDTO);
}
@Delete(':id')
deleteBranch(@Param('id') id: string) {
return this.branchesApplication.deleteBranch(Number(id));
}
@Post('activate')
activateBranches() {
return this.branchesApplication.activateBranches();
}
@Put(':id/mark-as-primary')
markBranchAsPrimary(@Param('id') id: string) {
return this.branchesApplication.markBranchAsPrimary(Number(id));
}
}

View File

@@ -0,0 +1,31 @@
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';
@Module({
imports: [TenancyDatabaseModule],
controllers: [BranchesController],
providers: [
CreateBranchService,
EditBranchService,
DeleteBranchService,
GetBranchService,
GetBranchesService,
MarkBranchAsPrimaryService,
ActivateBranches,
BranchesApplication,
TenancyContext,
TransformerInjectable,
],
})
export class BranchesModule {}

View File

@@ -0,0 +1,50 @@
import { Knex } from 'knex';
export interface IBranch {
id?: number;
}
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: IBranch;
trx: Knex.Transaction;
}
export interface IBranchMarkAsPrimaryPayload {
// tenantId: number;
oldBranch: IBranch;
trx: Knex.Transaction;
}
export interface IBranchMarkedAsPrimaryPayload {
// tenantId: number;
oldBranch: IBranch;
markedBranch: IBranch;
trx: Knex.Transaction;
}

View File

@@ -0,0 +1,99 @@
import { IBranch, 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';
@Injectable()
export class BranchesApplication {
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.
* @param {number} tenantId
* @returns {IBranch}
*/
// public getBranches = (): Promise<Branch[]> => {
// // return this.getBranchesService.getBranches(tenantId);
// };
/**
* Retrieves the given branch details.
* @param {number} branchId - Branch id.
* @returns {Promise<IBranch>}
*/
public getBranch = (branchId: number): Promise<IBranch> => {
return this.getBranchService.getBranch(branchId);
};
/**
* Creates a new branch.
* @param {number} tenantId -
* @param {ICreateBranchDTO} createBranchDTO
* @returns {Promise<IBranch>}
*/
public createBranch = (
createBranchDTO: ICreateBranchDTO,
): 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: IEditBranchDTO,
): 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,29 @@
import { Service, Inject } from 'typedi';
import { Features } from '@/interfaces';
import HasTenancyService from '@/services/Tenancy/TenancyService';
@Service()
export class BranchesSettings {
@Inject()
private tenancy: HasTenancyService;
/**
* Marks multi-branches as activated.
* @param {number} tenantId -
*/
public markMultiBranchesAsActivated = (tenantId: number) => {
const settings = this.tenancy.settings(tenantId);
settings.set({ group: 'features', key: Features.BRANCHES, value: 1 });
};
/**
* Retrieves whether multi-branches is active.
* @param {number} tenantId
*/
public isMultiBranchesActive = (tenantId: number) => {
const settings = this.tenancy.settings(tenantId);
return settings.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,80 @@
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 { BranchesSettings } 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';
@Injectable()
export class ActivateBranches {
constructor(
private readonly uow: UnitOfWork,
private readonly eventPublisher: EventEmitter2,
private readonly createBranch: CreateBranchService,
private readonly branchesSettings: BranchesSettings,
private readonly i18n: I18nService,
@Inject(Branch.name)
private readonly branchModel: typeof Branch,
) {}
/**
* 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 = (): Promise<void> => {
const isActivated = 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,50 @@
import { Inject, Injectable } from '@nestjs/common';
import { ERRORS } from '../constants';
import { Branch } from '../models/Branch.model';
import { ServiceError } from '../../Items/ServiceError';
@Injectable()
export class BranchCommandValidator {
constructor(
@Inject(Branch.name)
private readonly branchModel: 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,55 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import {
IBranchCreatedPayload,
IBranchCreatePayload,
ICreateBranchDTO,
} from '../Branches.types';
import { BranchCommandValidator } from './BranchCommandValidator.service';
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';
@Injectable()
export class CreateBranchService {
constructor(
private readonly uow: UnitOfWork,
private readonly eventPublisher: EventEmitter2,
private readonly validator: BranchCommandValidator,
@Inject(Branch.name)
private readonly branchModel: typeof Branch,
) {}
/**
* Creates a new branch.
* @param {ICreateBranchDTO} createBranchDTO
* @returns {Promise<IBranch>}
*/
public createBranch = async (
createBranchDTO: ICreateBranchDTO,
): 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,65 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import { IBranchDeletedPayload, IBranchDeletePayload } from './Branch.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';
@Injectable()
export class DeleteBranchService {
constructor(
@Inject(Branch.name)
private readonly branchModel: 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,61 @@
import { Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import {
IBranchEditedPayload,
IBranchEditPayload,
IEditBranchDTO,
} 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';
@Injectable()
export class EditBranchService {
constructor(
private readonly branchModel: 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: IEditBranchDTO,
) => {
// 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,62 @@
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';
@Injectable()
export class MarkBranchAsPrimaryService {
constructor(
private readonly uow: UnitOfWork,
private readonly eventPublisher: EventEmitter2,
@Inject(Branch.name)
private readonly branchModel: 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,35 @@
// import { Service, Inject } from 'typedi';
// import { omit } from 'lodash';
// import { BranchesSettings } from '../BranchesSettings';
// @Service()
// export class BranchTransactionDTOTransform {
// @Inject()
// branchesSettings: BranchesSettings;
// /**
// * Excludes DTO branch id when mutli-warehouses feature is inactive.
// * @param {number} tenantId
// * @returns {any}
// */
// private excludeDTOBranchIdWhenInactive = <T extends { branchId?: number }>(
// tenantId: number,
// DTO: T
// ): Omit<T, 'branchId'> | T => {
// const isActive = this.branchesSettings.isMultiBranchesActive(tenantId);
// return !isActive ? omit(DTO, ['branchId']) : DTO;
// };
// /**
// * Transformes the input DTO for branches feature.
// * @param {number} tenantId -
// * @param {T} DTO -
// * @returns {Omit<T, 'branchId'> | T}
// */
// public transformDTO =
// <T extends { branchId?: number }>(tenantId: number) =>
// (DTO: T): Omit<T, 'branchId'> | T => {
// return this.excludeDTOBranchIdWhenInactive<T>(tenantId, DTO);
// };
// }

View File

@@ -0,0 +1,26 @@
// import { Service, Inject } from 'typedi';
// import { Knex } from 'knex';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// @Service()
// export class CashflowTransactionsActivateBranches {
// @Inject()
// private tenancy: HasTenancyService;
// /**
// * Updates all cashflow transactions with the primary branch.
// * @param {number} tenantId
// * @param {number} primaryBranchId
// * @returns {Promise<void>}
// */
// public updateCashflowTransactionsWithBranch = async (
// tenantId: number,
// primaryBranchId: number,
// trx?: Knex.Transaction
// ) => {
// const { CashflowTransaction } = this.tenancy.models(tenantId);
// // Updates the cashflow transactions with primary branch.
// await CashflowTransaction.query(trx).update({ branchId: primaryBranchId });
// };
// }

View File

@@ -0,0 +1,26 @@
// import { Service, Inject } from 'typedi';
// import { Knex } from 'knex';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// @Service()
// export class ExpensesActivateBranches {
// @Inject()
// private tenancy: HasTenancyService;
// /**
// * Updates all expenses transactions with the primary branch.
// * @param {number} tenantId
// * @param {number} primaryBranchId
// * @returns {Promise<void>}
// */
// public updateExpensesWithBranch = async (
// tenantId: number,
// primaryBranchId: number,
// trx?: Knex.Transaction
// ) => {
// const { Expense } = this.tenancy.models(tenantId);
// // Updates the expenses with primary branch.
// await Expense.query(trx).update({ branchId: primaryBranchId });
// };
// }

View File

@@ -0,0 +1,26 @@
// import { Service, Inject } from 'typedi';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { Knex } from 'knex';
// @Service()
// export class ManualJournalsActivateBranches {
// @Inject()
// private tenancy: HasTenancyService;
// /**
// * Updates all manual journals transactions with the primary branch.
// * @param {number} tenantId
// * @param {number} primaryBranchId
// * @returns {Promise<void>}
// */
// public updateManualJournalsWithBranch = async (
// tenantId: number,
// primaryBranchId: number,
// trx?: Knex.Transaction
// ) => {
// const { ManualJournal } = this.tenancy.models(tenantId);
// // Updates the manual journal with primary branch.
// await ManualJournal.query(trx).update({ branchId: primaryBranchId });
// };
// }

View File

@@ -0,0 +1,32 @@
// import { omit } from 'lodash';
// import { Inject, Service } from 'typedi';
// import { IManualJournal } from '@/interfaces';
// import { BranchesSettings } from '../../BranchesSettings';
// @Service()
// export class ManualJournalBranchesDTOTransformer {
// @Inject()
// branchesSettings: BranchesSettings;
// private excludeDTOBranchIdWhenInactive = (
// tenantId: number,
// DTO: IManualJournal
// ): IManualJournal => {
// const isActive = this.branchesSettings.isMultiBranchesActive(tenantId);
// if (isActive) return DTO;
// return {
// ...DTO,
// entries: DTO.entries.map((e) => omit(e, ['branchId'])),
// };
// };
// /**
// *
// */
// public transformDTO =
// (tenantId: number) =>
// (DTO: IManualJournal): IManualJournal => {
// return this.excludeDTOBranchIdWhenInactive(tenantId, DTO);
// };
// }

View File

@@ -0,0 +1,23 @@
// import { Service, Inject } from 'typedi';
// import { ServiceError } from '@/exceptions';
// import { IManualJournalDTO, IManualJournalEntryDTO } from '@/interfaces';
// import { ERRORS } from './constants';
// @Service()
// 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,26 @@
// import { Service, Inject } from 'typedi';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { Knex } from 'knex';
// @Service()
// export class BillActivateBranches {
// @Inject()
// private tenancy: HasTenancyService;
// /**
// * Updates all bills transactions with the primary branch.
// * @param {number} tenantId
// * @param {number} primaryBranchId
// * @returns {Promise<void>}
// */
// public updateBillsWithBranch = async (
// tenantId: number,
// primaryBranchId: number,
// trx?: Knex.Transaction
// ) => {
// const { Bill } = this.tenancy.models(tenantId);
// // Updates the sale invoice with primary branch.
// await Bill.query(trx).update({ branchId: primaryBranchId });
// };
// }

View File

@@ -0,0 +1,26 @@
// import { Service, Inject } from 'typedi';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { Knex } from 'knex';
// @Service()
// export class BillPaymentsActivateBranches {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Updates all bills payments transcations with the primary branch.
// * @param {number} tenantId
// * @param {number} primaryBranchId
// * @returns {Promise<void>}
// */
// public updateBillPaymentsWithBranch = async (
// tenantId: number,
// primaryBranchId: number,
// trx?: Knex.Transaction
// ) => {
// const { BillPayment } = this.tenancy.models(tenantId);
// // Updates the bill payments with primary branch.
// await BillPayment.query(trx).update({ branchId: primaryBranchId });
// };
// }

View File

@@ -0,0 +1,26 @@
// import { Service, Inject } from 'typedi';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { Knex } from 'knex';
// @Service()
// export class VendorCreditActivateBranches {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Updates all vendor credits transcations with the primary branch.
// * @param {number} tenantId
// * @param {number} primaryBranchId
// * @returns {Promise<void>}
// */
// public updateVendorCreditsWithBranch = async (
// tenantId: number,
// primaryBranchId: number,
// trx?: Knex.Transaction
// ) => {
// const { VendorCredit } = this.tenancy.models(tenantId);
// // Updates the vendors credits with primary branch.
// await VendorCredit.query(trx).update({ branchId: primaryBranchId });
// };
// }

View File

@@ -0,0 +1,26 @@
// import { Service, Inject } from 'typedi';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { Knex } from 'knex';
// @Service()
// export class CreditNoteActivateBranches {
// @Inject()
// private tenancy: HasTenancyService;
// /**
// * Updates all creidt notes transactions with the primary branch.
// * @param {number} tenantId
// * @param {number} primaryBranchId
// * @returns {Promise<void>}
// */
// public updateCreditsWithBranch = async (
// tenantId: number,
// primaryBranchId: number,
// trx?: Knex.Transaction
// ) => {
// const { CreditNote } = this.tenancy.models(tenantId);
// // Updates the sale invoice with primary branch.
// await CreditNote.query(trx).update({ branchId: primaryBranchId });
// };
// }

View File

@@ -0,0 +1,26 @@
// import { Service, Inject } from 'typedi';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { Knex } from 'knex';
// @Service()
// export class PaymentReceiveActivateBranches {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Updates all creidt notes transactions with the primary branch.
// * @param {number} tenantId
// * @param {number} primaryBranchId
// * @returns {Promise<void>}
// */
// public updatePaymentsWithBranch = async (
// tenantId: number,
// primaryBranchId: number,
// trx?: Knex.Transaction
// ) => {
// const { PaymentReceive } = this.tenancy.models(tenantId);
// // Updates the sale invoice with primary branch.
// await PaymentReceive.query(trx).update({ branchId: primaryBranchId });
// };
// }

View File

@@ -0,0 +1,26 @@
// import { Service, Inject } from 'typedi';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { Knex } from 'knex';
// @Service()
// export class SaleEstimateActivateBranches {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Updates all sale estimates transactions with the primary branch.
// * @param {number} tenantId
// * @param {number} primaryBranchId
// * @returns {Promise<void>}
// */
// public updateEstimatesWithBranch = async (
// tenantId: number,
// primaryBranchId: number,
// trx?: Knex.Transaction
// ) => {
// const { PaymentReceive } = this.tenancy.models(tenantId);
// // Updates the sale invoice with primary branch.
// await PaymentReceive.query(trx).update({ branchId: primaryBranchId });
// };
// }

View File

@@ -0,0 +1,26 @@
// import { Service, Inject } from 'typedi';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { Knex } from 'knex';
// @Service()
// export class SaleInvoiceActivateBranches {
// @Inject()
// private tenancy: HasTenancyService;
// /**
// * Updates all sale invoices transactions with the primary branch.
// * @param {number} tenantId
// * @param {number} primaryBranchId
// * @returns {Promise<void>}
// */
// public updateInvoicesWithBranch = async (
// tenantId: number,
// primaryBranchId: number,
// trx?: Knex.Transaction
// ) => {
// const { SaleInvoice } = this.tenancy.models(tenantId);
// // Updates the sale invoice with primary branch.
// await SaleInvoice.query(trx).update({ branchId: primaryBranchId });
// };
// }

View File

@@ -0,0 +1,26 @@
// import { Service, Inject } from 'typedi';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { Knex } from 'knex';
// @Service()
// export class SaleReceiptActivateBranches {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Updates all sale receipts transactions with the primary branch.
// * @param {number} tenantId
// * @param {number} primaryBranchId
// * @returns {Promise<void>}
// */
// public updateReceiptsWithBranch = async (
// tenantId: number,
// primaryBranchId: number,
// trx?: Knex.Transaction
// ) => {
// const { SaleReceipt } = this.tenancy.models(tenantId);
// // Updates the sale receipt with primary branch.
// await SaleReceipt.query(trx).update({ branchId: primaryBranchId });
// };
// }

View File

@@ -0,0 +1,75 @@
// import { ServiceError } from '@/exceptions';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { Service, Inject } from 'typedi';
// import { BranchesSettings } from '../BranchesSettings';
// import { ERRORS } from './constants';
// @Service()
// export class ValidateBranchExistance {
// @Inject()
// tenancy: HasTenancyService;
// @Inject()
// branchesSettings: BranchesSettings;
// /**
// * Validate transaction branch id when the feature is active.
// * @param {number} tenantId
// * @param {number} branchId
// * @returns {Promise<void>}
// */
// public validateTransactionBranchWhenActive = async (
// tenantId: number,
// branchId: number | null
// ) => {
// const isActive = this.branchesSettings.isMultiBranchesActive(tenantId);
// // Can't continue if the multi-warehouses feature is inactive.
// if (!isActive) return;
// return this.validateTransactionBranch(tenantId, branchId);
// };
// /**
// * Validate transaction branch id existance.
// * @param {number} tenantId
// * @param {number} branchId
// * @return {Promise<void>}
// */
// public validateTransactionBranch = async (
// tenantId: number,
// branchId: number | null
// ) => {
// this.validateBranchIdExistance(branchId);
// await this.validateBranchExistance(tenantId, branchId);
// };
// /**
// *
// * @param branchId
// */
// public validateBranchIdExistance = (branchId: number | null) => {
// if (!branchId) {
// throw new ServiceError(ERRORS.BRANCH_ID_REQUIRED);
// }
// };
// /**
// *
// * @param tenantId
// * @param branchId
// */
// public validateBranchExistance = async (
// tenantId: number,
// branchId: number
// ) => {
// const { Branch } = this.tenancy.models(tenantId);
// const branch = await Branch.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,25 @@
import { Inject } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import { Branch } from '../models/Branch.model';
@Injectable()
export class GetBranchService {
constructor(
@Inject(Branch.name)
private readonly branch: 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,20 @@
import { Inject, Injectable } from '@nestjs/common';
import { Branch } from '../models/Branch.model';
@Injectable()
export class GetBranchesService {
constructor(
@Inject(Branch.name)
private readonly branch: 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,38 @@
// import { IBranchesActivatedPayload } from '@/interfaces';
// import { Service, Inject } from 'typedi';
// import events from '@/subscribers/events';
// import { CashflowTransactionsActivateBranches } from '../../Integrations/Cashflow/CashflowActivateBranches';
// @Service()
// export class CreditNoteActivateBranchesSubscriber {
// @Inject()
// private cashflowActivateBranches: CashflowTransactionsActivateBranches;
// /**
// * Attaches events with handlers.
// */
// public attach(bus) {
// bus.subscribe(
// events.branch.onActivated,
// this.updateCashflowWithBranchOnActivated
// );
// return bus;
// }
// /**
// * Updates accounts transactions with the primary branch once
// * the multi-branches is activated.
// * @param {IBranchesActivatedPayload}
// */
// private updateCashflowWithBranchOnActivated = async ({
// tenantId,
// primaryBranch,
// trx,
// }: IBranchesActivatedPayload) => {
// await this.cashflowActivateBranches.updateCashflowTransactionsWithBranch(
// tenantId,
// primaryBranch.id,
// trx
// );
// };
// }

View File

@@ -0,0 +1,38 @@
// import { IBranchesActivatedPayload } from '@/interfaces';
// import { Service, Inject } from 'typedi';
// import { CreditNoteActivateBranches } from '../../Integrations/Sales/CreditNoteBranchesActivate';
// import events from '@/subscribers/events';
// @Service()
// export class CreditNoteActivateBranchesSubscriber {
// @Inject()
// private creditNotesActivateBranches: CreditNoteActivateBranches;
// /**
// * Attaches events with handlers.
// */
// public attach(bus) {
// bus.subscribe(
// events.branch.onActivated,
// this.updateCreditNoteWithBranchOnActivated
// );
// return bus;
// }
// /**
// * Updates accounts transactions with the primary branch once
// * the multi-branches is activated.
// * @param {IBranchesActivatedPayload}
// */
// private updateCreditNoteWithBranchOnActivated = async ({
// tenantId,
// primaryBranch,
// trx,
// }: IBranchesActivatedPayload) => {
// await this.creditNotesActivateBranches.updateCreditsWithBranch(
// tenantId,
// primaryBranch.id,
// trx
// );
// };
// }

View File

@@ -0,0 +1,38 @@
// import { IBranchesActivatedPayload } from '@/interfaces';
// import { Service, Inject } from 'typedi';
// import events from '@/subscribers/events';
// import { ExpensesActivateBranches } from '../../Integrations/Expense/ExpensesActivateBranches';
// @Service()
// export class ExpenseActivateBranchesSubscriber {
// @Inject()
// private expensesActivateBranches: ExpensesActivateBranches;
// /**
// * Attaches events with handlers.
// */
// public attach(bus) {
// bus.subscribe(
// events.branch.onActivated,
// this.updateExpensesWithBranchOnActivated
// );
// return bus;
// }
// /**
// * Updates accounts transactions with the primary branch once
// * the multi-branches is activated.
// * @param {IBranchesActivatedPayload}
// */
// private updateExpensesWithBranchOnActivated = async ({
// tenantId,
// primaryBranch,
// trx,
// }: IBranchesActivatedPayload) => {
// await this.expensesActivateBranches.updateExpensesWithBranch(
// tenantId,
// primaryBranch.id,
// trx
// );
// };
// }

View File

@@ -0,0 +1,38 @@
// import { IBranchesActivatedPayload } from '@/interfaces';
// import { Service, Inject } from 'typedi';
// import events from '@/subscribers/events';
// import { BillPaymentsActivateBranches } from '../../Integrations/Purchases/PaymentMadeBranchesActivate';
// @Service()
// export class PaymentMadeActivateBranchesSubscriber {
// @Inject()
// private paymentsActivateBranches: BillPaymentsActivateBranches;
// /**
// * Attaches events with handlers.
// */
// public attach(bus) {
// bus.subscribe(
// events.branch.onActivated,
// this.updatePaymentsWithBranchOnActivated
// );
// return bus;
// }
// /**
// * Updates accounts transactions with the primary branch once
// * the multi-branches is activated.
// * @param {IBranchesActivatedPayload}
// */
// private updatePaymentsWithBranchOnActivated = async ({
// tenantId,
// primaryBranch,
// trx,
// }: IBranchesActivatedPayload) => {
// await this.paymentsActivateBranches.updateBillPaymentsWithBranch(
// tenantId,
// primaryBranch.id,
// trx
// );
// };
// }

View File

@@ -0,0 +1,38 @@
// import { IBranchesActivatedPayload } from '@/interfaces';
// import { Service, Inject } from 'typedi';
// import events from '@/subscribers/events';
// import { PaymentReceiveActivateBranches } from '../../Integrations/Sales/PaymentReceiveBranchesActivate';
// @Service()
// export class PaymentReceiveActivateBranchesSubscriber {
// @Inject()
// private paymentsActivateBranches: PaymentReceiveActivateBranches;
// /**
// * Attaches events with handlers.
// */
// public attach(bus) {
// bus.subscribe(
// events.branch.onActivated,
// this.updateCreditNoteWithBranchOnActivated
// );
// return bus;
// }
// /**
// * Updates accounts transactions with the primary branch once
// * the multi-branches is activated.
// * @param {IBranchesActivatedPayload}
// */
// private updateCreditNoteWithBranchOnActivated = async ({
// tenantId,
// primaryBranch,
// trx,
// }: IBranchesActivatedPayload) => {
// await this.paymentsActivateBranches.updatePaymentsWithBranch(
// tenantId,
// primaryBranch.id,
// trx
// );
// };
// }

View File

@@ -0,0 +1,38 @@
// import { IBranchesActivatedPayload } from '@/interfaces';
// import { Service, Inject } from 'typedi';
// import events from '@/subscribers/events';
// import { SaleEstimateActivateBranches } from '../../Integrations/Sales/SaleEstimatesBranchesActivate';
// @Service()
// export class SaleEstimatesActivateBranchesSubscriber {
// @Inject()
// private estimatesActivateBranches: SaleEstimateActivateBranches;
// /**
// * Attaches events with handlers.
// */
// public attach(bus) {
// bus.subscribe(
// events.branch.onActivated,
// this.updateEstimatesWithBranchOnActivated
// );
// return bus;
// }
// /**
// * Updates accounts transactions with the primary branch once
// * the multi-branches is activated.
// * @param {IBranchesActivatedPayload}
// */
// private updateEstimatesWithBranchOnActivated = async ({
// tenantId,
// primaryBranch,
// trx,
// }: IBranchesActivatedPayload) => {
// await this.estimatesActivateBranches.updateEstimatesWithBranch(
// tenantId,
// primaryBranch.id,
// trx
// );
// };
// }

View File

@@ -0,0 +1,38 @@
// import { IBranchesActivatedPayload } from '@/interfaces';
// import { Service, Inject } from 'typedi';
// import events from '@/subscribers/events';
// import { SaleInvoiceActivateBranches } from '../../Integrations/Sales/SaleInvoiceBranchesActivate';
// @Service()
// export class SaleInvoicesActivateBranchesSubscriber {
// @Inject()
// private invoicesActivateBranches: SaleInvoiceActivateBranches;
// /**
// * Attaches events with handlers.
// */
// public attach(bus) {
// bus.subscribe(
// events.branch.onActivated,
// this.updateInvoicesWithBranchOnActivated
// );
// return bus;
// }
// /**
// * Updates accounts transactions with the primary branch once
// * the multi-branches is activated.
// * @param {IBranchesActivatedPayload}
// */
// private updateInvoicesWithBranchOnActivated = async ({
// tenantId,
// primaryBranch,
// trx,
// }: IBranchesActivatedPayload) => {
// await this.invoicesActivateBranches.updateInvoicesWithBranch(
// tenantId,
// primaryBranch.id,
// trx
// );
// };
// }

View File

@@ -0,0 +1,38 @@
// import { IBranchesActivatedPayload } from '@/interfaces';
// import { Service, Inject } from 'typedi';
// import events from '@/subscribers/events';
// import { SaleReceiptActivateBranches } from '../../Integrations/Sales/SaleReceiptBranchesActivate';
// @Service()
// export class SaleReceiptsActivateBranchesSubscriber {
// @Inject()
// private receiptsActivateBranches: SaleReceiptActivateBranches;
// /**
// * Attaches events with handlers.
// */
// public attach(bus) {
// bus.subscribe(
// events.branch.onActivated,
// this.updateReceiptsWithBranchOnActivated
// );
// return bus;
// }
// /**
// * Updates accounts transactions with the primary branch once
// * the multi-branches is activated.
// * @param {IBranchesActivatedPayload}
// */
// private updateReceiptsWithBranchOnActivated = async ({
// tenantId,
// primaryBranch,
// trx,
// }: IBranchesActivatedPayload) => {
// await this.receiptsActivateBranches.updateReceiptsWithBranch(
// tenantId,
// primaryBranch.id,
// trx
// );
// };
// }

View File

@@ -0,0 +1,8 @@
// export * from './CashflowBranchesActivateSubscriber';
// export * from './CreditNoteBranchesActivateSubscriber';
// export * from './PaymentMadeBranchesActivateSubscriber';
// export * from './PaymentReceiveBranchesActivateSubscriber';
// export * from './SaleReceiptsBranchesActivateSubscriber';
// export * from './SaleEstiamtesBranchesActivateSubscriber';
// export * from './SaleInvoiceBranchesActivateSubscriber';
// export * from './ExpenseBranchesActivateSubscriber';

View File

@@ -0,0 +1,53 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import { IBillCreatingPayload, IBillEditingPayload } from '@/interfaces';
// import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
// @Service()
// export class BillBranchValidateSubscriber {
// @Inject()
// private validateBranchExistance: ValidateBranchExistance;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.bill.onCreating,
// this.validateBranchExistanceOnBillCreating
// );
// bus.subscribe(
// events.bill.onEditing,
// this.validateBranchExistanceOnBillEditing
// );
// return bus;
// };
// /**
// * Validate branch existance on estimate creating.
// * @param {ISaleEstimateCreatedPayload} payload
// */
// private validateBranchExistanceOnBillCreating = async ({
// tenantId,
// billDTO,
// }: IBillCreatingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// billDTO.branchId
// );
// };
// /**
// * Validate branch existance once estimate editing.
// * @param {ISaleEstimateEditingPayload} payload
// */
// private validateBranchExistanceOnBillEditing = async ({
// billDTO,
// tenantId,
// }: IBillEditingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// billDTO.branchId
// );
// };
// }

View File

@@ -0,0 +1,35 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import { ICommandCashflowCreatingPayload } from '@/interfaces';
// import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
// @Service()
// export class CashflowBranchDTOValidatorSubscriber {
// @Inject()
// private validateBranchExistance: ValidateBranchExistance;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.cashflow.onTransactionCreating,
// this.validateBranchExistanceOnCashflowTransactionCreating
// );
// return bus;
// };
// /**
// * Validate branch existance once cashflow transaction creating.
// * @param {ICommandCashflowCreatingPayload} payload
// */
// private validateBranchExistanceOnCashflowTransactionCreating = async ({
// tenantId,
// newTransactionDTO,
// }: ICommandCashflowCreatingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// newTransactionDTO.branchId
// );
// };
// }

View File

@@ -0,0 +1,104 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import {
// ICustomerEventCreatingPayload,
// ICustomerOpeningBalanceEditingPayload,
// IVendorEventCreatingPayload,
// IVendorOpeningBalanceEditingPayload,
// } from '@/interfaces';
// import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
// @Service()
// export class ContactBranchValidateSubscriber {
// @Inject()
// private validateBranchExistance: ValidateBranchExistance;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.customers.onCreating,
// this.validateBranchExistanceOnCustomerCreating
// );
// bus.subscribe(
// events.customers.onOpeningBalanceChanging,
// this.validateBranchExistanceOnCustomerOpeningBalanceEditing
// );
// bus.subscribe(
// events.vendors.onCreating,
// this.validateBranchExistanceonVendorCreating
// );
// bus.subscribe(
// events.vendors.onOpeningBalanceChanging,
// this.validateBranchExistanceOnVendorOpeningBalanceEditing
// );
// return bus;
// };
// /**
// * Validate branch existance on customer creating.
// * @param {ICustomerEventCreatingPayload} payload
// */
// private validateBranchExistanceOnCustomerCreating = async ({
// tenantId,
// customerDTO,
// }: ICustomerEventCreatingPayload) => {
// // Can't continue if the customer opening balance is zero.
// if (!customerDTO.openingBalance) return;
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// customerDTO.openingBalanceBranchId
// );
// };
// /**
// * Validate branch existance once customer opening balance editing.
// * @param {ICustomerOpeningBalanceEditingPayload} payload
// */
// private validateBranchExistanceOnCustomerOpeningBalanceEditing = async ({
// openingBalanceEditDTO,
// tenantId,
// }: ICustomerOpeningBalanceEditingPayload) => {
// if (!openingBalanceEditDTO.openingBalance) return;
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// openingBalanceEditDTO.openingBalanceBranchId
// );
// };
// /**
// * Validates the branch existance on vendor creating.
// * @param {IVendorEventCreatingPayload} payload -
// */
// private validateBranchExistanceonVendorCreating = async ({
// vendorDTO,
// tenantId,
// }: IVendorEventCreatingPayload) => {
// // Can't continue if the customer opening balance is zero.
// if (!vendorDTO.openingBalance) return;
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// vendorDTO.openingBalanceBranchId
// );
// };
// /**
// * Validate branch existance once the vendor opening balance editing.
// * @param {IVendorOpeningBalanceEditingPayload}
// */
// private validateBranchExistanceOnVendorOpeningBalanceEditing = async ({
// tenantId,
// openingBalanceEditDTO,
// }: IVendorOpeningBalanceEditingPayload) => {
// if (!openingBalanceEditDTO.openingBalance) return;
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// openingBalanceEditDTO.openingBalanceBranchId
// );
// };
// }

View File

@@ -0,0 +1,56 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import {
// ICreditNoteCreatingPayload,
// ICreditNoteEditingPayload,
// } from '@/interfaces';
// import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
// @Service()
// export class CreditNoteBranchValidateSubscriber {
// @Inject()
// private validateBranchExistance: ValidateBranchExistance;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.creditNote.onCreating,
// this.validateBranchExistanceOnCreditCreating
// );
// bus.subscribe(
// events.creditNote.onEditing,
// this.validateBranchExistanceOnCreditEditing
// );
// return bus;
// };
// /**
// * Validate branch existance on estimate creating.
// * @param {ICreditNoteCreatingPayload} payload
// */
// private validateBranchExistanceOnCreditCreating = async ({
// tenantId,
// creditNoteDTO,
// }: ICreditNoteCreatingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// creditNoteDTO.branchId
// );
// };
// /**
// * Validate branch existance once estimate editing.
// * @param {ISaleEstimateEditingPayload} payload
// */
// private validateBranchExistanceOnCreditEditing = async ({
// creditNoteEditDTO,
// tenantId,
// }: ICreditNoteEditingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// creditNoteEditDTO.branchId
// );
// };
// }

View File

@@ -0,0 +1,35 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import { IRefundCreditNoteCreatingPayload } from '@/interfaces';
// import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
// @Service()
// export class CreditNoteRefundBranchValidateSubscriber {
// @Inject()
// private validateBranchExistance: ValidateBranchExistance;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.creditNote.onRefundCreating,
// this.validateBranchExistanceOnCreditRefundCreating
// );
// return bus;
// };
// /**
// * Validate branch existance on refund credit note creating.
// * @param {ICreditNoteCreatingPayload} payload
// */
// private validateBranchExistanceOnCreditRefundCreating = async ({
// tenantId,
// newCreditNoteDTO,
// }: IRefundCreditNoteCreatingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// newCreditNoteDTO.branchId
// );
// };
// }

View File

@@ -0,0 +1,56 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import {
// IExpenseCreatingPayload,
// IExpenseEventEditingPayload,
// } from '@/interfaces';
// import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
// @Service()
// export class ExpenseBranchValidateSubscriber {
// @Inject()
// private validateBranchExistance: ValidateBranchExistance;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.expenses.onCreating,
// this.validateBranchExistanceOnExpenseCreating
// );
// bus.subscribe(
// events.expenses.onEditing,
// this.validateBranchExistanceOnExpenseEditing
// );
// return bus;
// };
// /**
// * Validate branch existance once expense transaction creating.
// * @param {ISaleEstimateCreatedPayload} payload
// */
// private validateBranchExistanceOnExpenseCreating = async ({
// tenantId,
// expenseDTO,
// }: IExpenseCreatingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// expenseDTO.branchId
// );
// };
// /**
// * Validate branch existance once expense transaction editing.
// * @param {ISaleEstimateEditingPayload} payload
// */
// private validateBranchExistanceOnExpenseEditing = async ({
// expenseDTO,
// tenantId,
// }: IExpenseEventEditingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// expenseDTO.branchId
// );
// };
// }

View File

@@ -0,0 +1,35 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import { IInventoryAdjustmentCreatingPayload } from '@/interfaces';
// import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
// @Service()
// export class InventoryAdjustmentBranchValidateSubscriber {
// @Inject()
// private validateBranchExistance: ValidateBranchExistance;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.inventoryAdjustment.onQuickCreating,
// this.validateBranchExistanceOnInventoryCreating
// );
// return bus;
// };
// /**
// * Validate branch existance on invoice creating.
// * @param {ISaleInvoiceCreatingPaylaod} payload
// */
// private validateBranchExistanceOnInventoryCreating = async ({
// tenantId,
// quickAdjustmentDTO,
// }: IInventoryAdjustmentCreatingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// quickAdjustmentDTO.branchId
// );
// };
// }

View File

@@ -0,0 +1,56 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import {
// ISaleInvoiceCreatingPaylaod,
// ISaleInvoiceEditingPayload,
// } from '@/interfaces';
// import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
// @Service()
// export class InvoiceBranchValidateSubscriber {
// @Inject()
// private validateBranchExistance: ValidateBranchExistance;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.saleInvoice.onCreating,
// this.validateBranchExistanceOnInvoiceCreating
// );
// bus.subscribe(
// events.saleInvoice.onEditing,
// this.validateBranchExistanceOnInvoiceEditing
// );
// return bus;
// };
// /**
// * Validate branch existance on invoice creating.
// * @param {ISaleInvoiceCreatingPaylaod} payload
// */
// private validateBranchExistanceOnInvoiceCreating = async ({
// tenantId,
// saleInvoiceDTO,
// }: ISaleInvoiceCreatingPaylaod) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// saleInvoiceDTO.branchId
// );
// };
// /**
// * Validate branch existance once invoice editing.
// * @param {ISaleInvoiceEditingPayload} payload
// */
// private validateBranchExistanceOnInvoiceEditing = async ({
// saleInvoiceDTO,
// tenantId,
// }: ISaleInvoiceEditingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// saleInvoiceDTO.branchId
// );
// };
// }

View File

@@ -0,0 +1,76 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import {
// Features,
// IManualJournalCreatingPayload,
// IManualJournalEditingPayload,
// } from '@/interfaces';
// import { ManualJournalBranchesValidator } from '../../Integrations/ManualJournals/ManualJournalsBranchesValidator';
// import { FeaturesManager } from '@/services/Features/FeaturesManager';
// @Service()
// export class ManualJournalBranchValidateSubscriber {
// @Inject()
// private validateManualJournalBranch: ManualJournalBranchesValidator;
// @Inject()
// private featuresManager: FeaturesManager;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.manualJournals.onCreating,
// this.validateBranchExistanceOnBillCreating
// );
// bus.subscribe(
// events.manualJournals.onEditing,
// this.validateBranchExistanceOnBillEditing
// );
// return bus;
// };
// /**
// * Validate branch existance on estimate creating.
// * @param {IManualJournalCreatingPayload} payload
// */
// private validateBranchExistanceOnBillCreating = async ({
// manualJournalDTO,
// tenantId,
// }: IManualJournalCreatingPayload) => {
// // Detarmines whether the multi-branches is accessible by tenant.
// const isAccessible = await this.featuresManager.accessible(
// tenantId,
// 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
// */
// private validateBranchExistanceOnBillEditing = async ({
// tenantId,
// manualJournalDTO,
// }: IManualJournalEditingPayload) => {
// // Detarmines whether the multi-branches is accessible by tenant.
// const isAccessible = await this.featuresManager.accessible(
// tenantId,
// 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,56 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import {
// IBillPaymentCreatingPayload,
// IBillPaymentEditingPayload,
// } from '@/interfaces';
// import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
// @Service()
// export class PaymentMadeBranchValidateSubscriber {
// @Inject()
// private validateBranchExistance: ValidateBranchExistance;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.billPayment.onCreating,
// this.validateBranchExistanceOnPaymentCreating
// );
// bus.subscribe(
// events.billPayment.onEditing,
// this.validateBranchExistanceOnPaymentEditing
// );
// return bus;
// };
// /**
// * Validate branch existance on estimate creating.
// * @param {ISaleEstimateCreatedPayload} payload
// */
// private validateBranchExistanceOnPaymentCreating = async ({
// tenantId,
// billPaymentDTO,
// }: IBillPaymentCreatingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// billPaymentDTO.branchId
// );
// };
// /**
// * Validate branch existance once estimate editing.
// * @param {ISaleEstimateEditingPayload} payload
// */
// private validateBranchExistanceOnPaymentEditing = async ({
// billPaymentDTO,
// tenantId,
// }: IBillPaymentEditingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// billPaymentDTO.branchId
// );
// };
// }

View File

@@ -0,0 +1,56 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import {
// IPaymentReceivedCreatingPayload,
// IPaymentReceivedEditingPayload,
// } from '@/interfaces';
// import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
// @Service()
// export class PaymentReceiveBranchValidateSubscriber {
// @Inject()
// private validateBranchExistance: ValidateBranchExistance;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.paymentReceive.onCreating,
// this.validateBranchExistanceOnPaymentCreating
// );
// bus.subscribe(
// events.paymentReceive.onEditing,
// this.validateBranchExistanceOnPaymentEditing
// );
// return bus;
// };
// /**
// * Validate branch existance on estimate creating.
// * @param {IPaymentReceivedCreatingPayload} payload
// */
// private validateBranchExistanceOnPaymentCreating = async ({
// tenantId,
// paymentReceiveDTO,
// }: IPaymentReceivedCreatingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// paymentReceiveDTO.branchId
// );
// };
// /**
// * Validate branch existance once estimate editing.
// * @param {IPaymentReceivedEditingPayload} payload
// */
// private validateBranchExistanceOnPaymentEditing = async ({
// paymentReceiveDTO,
// tenantId,
// }: IPaymentReceivedEditingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// paymentReceiveDTO.branchId
// );
// };
// }

View File

@@ -0,0 +1,56 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import {
// ISaleEstimateCreatingPayload,
// ISaleEstimateEditingPayload,
// } from '@/interfaces';
// import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
// @Service()
// export class SaleEstimateBranchValidateSubscriber {
// @Inject()
// private validateBranchExistance: ValidateBranchExistance;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.saleEstimate.onCreating,
// this.validateBranchExistanceOnEstimateCreating
// );
// bus.subscribe(
// events.saleEstimate.onEditing,
// this.validateBranchExistanceOnEstimateEditing
// );
// return bus;
// };
// /**
// * Validate branch existance on estimate creating.
// * @param {ISaleEstimateCreatedPayload} payload
// */
// private validateBranchExistanceOnEstimateCreating = async ({
// tenantId,
// estimateDTO,
// }: ISaleEstimateCreatingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// estimateDTO.branchId
// );
// };
// /**
// * Validate branch existance once estimate editing.
// * @param {ISaleEstimateEditingPayload} payload
// */
// private validateBranchExistanceOnEstimateEditing = async ({
// estimateDTO,
// tenantId,
// }: ISaleEstimateEditingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// estimateDTO.branchId
// );
// };
// }

View File

@@ -0,0 +1,56 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import {
// ISaleReceiptCreatingPayload,
// ISaleReceiptEditingPayload,
// } from '@/interfaces';
// import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
// @Service()
// export class SaleReceiptBranchValidateSubscriber {
// @Inject()
// private validateBranchExistance: ValidateBranchExistance;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.saleReceipt.onCreating,
// this.validateBranchExistanceOnInvoiceCreating
// );
// bus.subscribe(
// events.saleReceipt.onEditing,
// this.validateBranchExistanceOnInvoiceEditing
// );
// return bus;
// };
// /**
// * Validate branch existance on estimate creating.
// * @param {ISaleReceiptCreatingPayload} payload
// */
// private validateBranchExistanceOnInvoiceCreating = async ({
// tenantId,
// saleReceiptDTO,
// }: ISaleReceiptCreatingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// saleReceiptDTO.branchId
// );
// };
// /**
// * Validate branch existance once estimate editing.
// * @param {ISaleReceiptEditingPayload} payload
// */
// private validateBranchExistanceOnInvoiceEditing = async ({
// saleReceiptDTO,
// tenantId,
// }: ISaleReceiptEditingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// saleReceiptDTO.branchId
// );
// };
// }

View File

@@ -0,0 +1,56 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import {
// IVendorCreditCreatingPayload,
// IVendorCreditEditingPayload,
// } from '@/interfaces';
// import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
// @Service()
// export class VendorCreditBranchValidateSubscriber {
// @Inject()
// private validateBranchExistance: ValidateBranchExistance;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.vendorCredit.onCreating,
// this.validateBranchExistanceOnCreditCreating
// );
// bus.subscribe(
// events.vendorCredit.onEditing,
// this.validateBranchExistanceOnCreditEditing
// );
// return bus;
// };
// /**
// * Validate branch existance on estimate creating.
// * @param {ISaleEstimateCreatedPayload} payload
// */
// private validateBranchExistanceOnCreditCreating = async ({
// tenantId,
// vendorCreditCreateDTO,
// }: IVendorCreditCreatingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// vendorCreditCreateDTO.branchId
// );
// };
// /**
// * Validate branch existance once estimate editing.
// * @param {ISaleEstimateEditingPayload} payload
// */
// private validateBranchExistanceOnCreditEditing = async ({
// vendorCreditDTO,
// tenantId,
// }: IVendorCreditEditingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// vendorCreditDTO.branchId
// );
// };
// }

View File

@@ -0,0 +1,35 @@
// import { Inject, Service } from 'typedi';
// import events from '@/subscribers/events';
// import { IRefundVendorCreditCreatingPayload } from '@/interfaces';
// import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance';
// @Service()
// export class VendorCreditRefundBranchValidateSubscriber {
// @Inject()
// private validateBranchExistance: ValidateBranchExistance;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.vendorCredit.onRefundCreating,
// this.validateBranchExistanceOnCreditRefundCreating
// );
// return bus;
// };
// /**
// * Validate branch existance on refund credit note creating.
// * @param {IRefundVendorCreditCreatingPayload} payload
// */
// private validateBranchExistanceOnCreditRefundCreating = async ({
// tenantId,
// refundVendorCreditDTO,
// }: IRefundVendorCreditCreatingPayload) => {
// await this.validateBranchExistance.validateTransactionBranchWhenActive(
// tenantId,
// refundVendorCreditDTO.branchId
// );
// };
// }

View File

@@ -0,0 +1,15 @@
export * from './BillBranchSubscriber';
export * from './CashflowBranchDTOValidatorSubscriber';
export * from './CreditNoteBranchesSubscriber';
export * from './CreditNoteRefundBranchSubscriber';
export * from './ExpenseBranchSubscriber';
export * from './ManualJournalBranchSubscriber';
export * from './PaymentMadeBranchSubscriber';
export * from './PaymentReceiveBranchSubscriber';
export * from './SaleEstimateMultiBranchesSubscriber';
export * from './SaleReceiptBranchesSubscriber';
export * from './VendorCreditBranchSubscriber';
export * from './VendorCreditRefundBranchSubscriber';
export * from './InvoiceBranchValidatorSubscriber';
export * from './ContactOpeningBalanceBranchSubscriber';
export * from './InventoryAdjustmentBranchValidatorSubscriber';

View File

@@ -15,6 +15,14 @@ import { events } from '@/common/events/events';
@Injectable()
export class CreateExpense {
/**
* @param {EventEmitter2} eventEmitter - Event emitter.
* @param {UnitOfWork} uow - Unit of work.
* @param {CommandExpenseValidator} validator - Command expense validator.
* @param {ExpenseDTOTransformer} transformDTO - Expense DTO transformer.
* @param {typeof Account} accountModel - Account model.
* @param {typeof Expense} expenseModel - Expense model.
*/
constructor(
private readonly eventEmitter: EventEmitter2,
private readonly uow: UnitOfWork,

View File

@@ -13,6 +13,13 @@ import {
@Injectable()
export class DeleteExpense {
/**
* @param {EventEmitter2} eventEmitter - Event emitter.
* @param {UnitOfWork} uow - Unit of work.
* @param {CommandExpenseValidator} validator - Command expense validator.
* @param {typeof Expense} expenseModel - Expense model.
* @param {typeof ExpenseCategory} expenseCategoryModel - Expense category model.
*/
constructor(
private readonly eventEmitter: EventEmitter2,
private readonly uow: UnitOfWork,

View File

@@ -16,6 +16,14 @@ import { events } from '@/common/events/events';
@Injectable()
export class EditExpense {
/**
* @param {EventEmitter2} eventEmitter - Event emitter.
* @param {UnitOfWork} uow - Unit of work.
* @param {CommandExpenseValidator} validator - Command expense validator.
* @param {ExpenseDTOTransformer} transformDTO - Expense DTO transformer.
* @param {typeof Expense} expenseModel - Expense model.
* @param {typeof Account} accountModel - Account model.
*/
constructor(
private eventEmitter: EventEmitter2,
private uow: UnitOfWork,

View File

@@ -12,6 +12,12 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
@Injectable()
export class PublishExpense {
/**
* @param {EventEmitter2} eventPublisher - Event emitter.
* @param {UnitOfWork} uow - Unit of work.
* @param {CommandExpenseValidator} validator - Command expense validator.
* @param {typeof Expense} expenseModel - Expense model.
*/
constructor(
private readonly eventPublisher: EventEmitter2,
private readonly uow: UnitOfWork,

View File

@@ -7,6 +7,12 @@ import { GetItemCategoryService } from './queries/GetItemCategory.service';
@Injectable()
export class ItemCategoryApplication {
/**
* @param {CreateItemCategoryService} createItemCategoryService - Create item category service.
* @param {EditItemCategoryService} editItemCategoryService - Edit item category service.
* @param {GetItemCategoryService} getItemCategoryService - Get item category service.
* @param {DeleteItemCategoryService} deleteItemCategoryService - Delete item category service.
*/
constructor(
private readonly createItemCategoryService: CreateItemCategoryService,
private readonly editItemCategoryService: EditItemCategoryService,

View File

@@ -7,6 +7,10 @@ import { ACCOUNT_ROOT_TYPE, ACCOUNT_TYPE } from '@/constants/accounts';
@Injectable()
export class CommandItemCategoryValidatorService {
/**
* @param {typeof ItemCategory} itemCategoryModel - Item category model.
* @param {typeof Account} accountModel - Account model.
*/
constructor(
@Inject(ItemCategory.name)
private readonly itemCategoryModel: typeof ItemCategory,

View File

@@ -13,6 +13,12 @@ import { SystemUser } from '@/modules/System/models/SystemUser';
@Injectable()
export class CreateItemCategoryService {
/**
* @param {UnitOfWork} uow - Unit of work.
* @param {CommandItemCategoryValidatorService} validator - Command item category validator service.
* @param {EventEmitter2} eventEmitter - Event emitter.
* @param {typeof ItemCategory} itemCategoryModel - Item category model.
*/
constructor(
private readonly uow: UnitOfWork,
private readonly validator: CommandItemCategoryValidatorService,

View File

@@ -10,6 +10,12 @@ import { Item } from '@/modules/Items/models/Item';
@Injectable()
export class DeleteItemCategoryService {
/**
* @param {UnitOfWork} uow - Unit of work.
* @param {CommandItemCategoryValidatorService} validator - Command item category validator service.
* @param {EventEmitter2} eventEmitter - Event emitter.
* @param {typeof ItemCategory} itemCategoryModel - Item category model.
*/
constructor(
private readonly uow: UnitOfWork,
private readonly validator: CommandItemCategoryValidatorService,

View File

@@ -13,6 +13,13 @@ import { Inject } from '@nestjs/common';
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
export class EditItemCategoryService {
/**
* @param {UnitOfWork} uow - Unit of work.
* @param {CommandItemCategoryValidatorService} validator - Command item category validator service.
* @param {EventEmitter2} eventEmitter - Event emitter.
* @param {TenancyContext} tenancyContext - Tenancy context.
* @param {typeof ItemCategory} itemCategoryModel - Item category model.
*/
constructor(
private readonly uow: UnitOfWork,
private readonly validator: CommandItemCategoryValidatorService,
@@ -21,6 +28,7 @@ export class EditItemCategoryService {
@Inject(ItemCategory.name)
private readonly itemCategoryModel: typeof ItemCategory,
) {}
/**
* Edits item category.
* @param {number} tenantId

View File

@@ -3,6 +3,9 @@ import { ItemCategory } from '../models/ItemCategory.model';
@Injectable()
export class GetItemCategoryService {
/**
* @param {typeof ItemCategory} itemCategoryModel - Item category model.
*/
constructor(
@Inject(ItemCategory.name)
private readonly itemCategoryModel: typeof ItemCategory,

View File

@@ -6,6 +6,10 @@ import { TransformerContext } from './Transformer.types';
@Injectable()
export class TransformerInjectable {
/**
* @param {TenancyContext} tenancyContext - Tenancy context.
* @param {I18nService} i18n - I18n service.
*/
constructor(
private readonly tenancyContext: TenancyContext,
private readonly i18n: I18nService,

View File

@@ -0,0 +1,30 @@
// import { Service, Inject } from 'typedi';
// import { IWarehouse } from '@/interfaces';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// @Service()
// export class BillActivateWarehouses {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Updates all credit note transactions with the primary warehouse.
// * @param {number} tenantId
// * @param {number} primaryWarehouse
// * @returns {Promise<void>}
// */
// public updateBillsWithWarehouse = async (
// tenantId: number,
// primaryWarehouse: IWarehouse
// ): Promise<void> => {
// const { Bill, ItemEntry } = this.tenancy.models(tenantId);
// // Updates the sale estimates with primary warehouse.
// await Bill.query().update({ warehouseId: primaryWarehouse.id });
// // Update the sale estimates entries with primary warehouse.
// await ItemEntry.query().where('referenceType', 'Bill').update({
// warehouseId: primaryWarehouse.id,
// });
// };
// }

View File

@@ -0,0 +1,30 @@
// import { Service, Inject } from 'typedi';
// import { IWarehouse } from '@/interfaces';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// @Service()
// export class CreditNotesActivateWarehouses {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Updates all credit note transactions with the primary warehouse.
// * @param {number} tenantId
// * @param {number} primaryWarehouse
// * @returns {Promise<void>}
// */
// public updateCreditsWithWarehouse = async (
// tenantId: number,
// primaryWarehouse: IWarehouse
// ): Promise<void> => {
// const { CreditNote, ItemEntry } = this.tenancy.models(tenantId);
// // Updates the sale estimates with primary warehouse.
// await CreditNote.query().update({ warehouseId: primaryWarehouse.id });
// // Update the sale estimates entries with primary warehouse.
// await ItemEntry.query().where('referenceType', 'CreditNote').update({
// warehouseId: primaryWarehouse.id,
// });
// };
// }

View File

@@ -0,0 +1,30 @@
// import { Service, Inject } from 'typedi';
// import { IWarehouse } from '@/interfaces';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// @Service()
// export class EstimatesActivateWarehouses {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Updates all inventory transactions with the primary warehouse.
// * @param {number} tenantId
// * @param {number} primaryWarehouse
// * @returns {Promise<void>}
// */
// public updateEstimatesWithWarehouse = async (
// tenantId: number,
// primaryWarehouse: IWarehouse
// ): Promise<void> => {
// const { SaleEstimate, ItemEntry } = this.tenancy.models(tenantId);
// // Updates the sale estimates with primary warehouse.
// await SaleEstimate.query().update({ warehouseId: primaryWarehouse.id });
// // Update the sale estimates entries with primary warehouse.
// await ItemEntry.query().where('referenceType', 'SaleEstimate').update({
// warehouseId: primaryWarehouse.id,
// });
// };
// }

View File

@@ -0,0 +1,31 @@
// import { Service, Inject } from 'typedi';
// import { IWarehouse } from '@/interfaces';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// @Service()
// export class InventoryActivateWarehouses {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Updates all inventory transactions with the primary warehouse.
// * @param {number} tenantId
// * @param {number} primaryWarehouse
// * @returns {Promise<void>}
// */
// public updateInventoryTransactionsWithWarehouse = async (
// tenantId: number,
// primaryWarehouse: IWarehouse
// ): Promise<void> => {
// const { InventoryTransaction, InventoryCostLotTracker } =
// this.tenancy.models(tenantId);
// // Updates the inventory transactions with primary warehouse.
// await InventoryTransaction.query().update({
// warehouseId: primaryWarehouse.id,
// });
// await InventoryCostLotTracker.query().update({
// warehouseId: primaryWarehouse.id,
// });
// };
// }

View File

@@ -0,0 +1,30 @@
// import { Service, Inject } from 'typedi';
// import { IWarehouse } from '@/interfaces';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// @Service()
// export class InvoicesActivateWarehouses {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Updates all inventory transactions with the primary warehouse.
// * @param {number} tenantId
// * @param {number} primaryWarehouse
// * @returns {Promise<void>}
// */
// public updateInvoicesWithWarehouse = async (
// tenantId: number,
// primaryWarehouse: IWarehouse
// ): Promise<void> => {
// const { SaleInvoice, ItemEntry } = this.tenancy.models(tenantId);
// // Updates the sale invoices with primary warehouse.
// await SaleInvoice.query().update({ warehouseId: primaryWarehouse.id });
// // Update the sale invoices entries with primary warehouse.
// await ItemEntry.query().where('referenceType', 'SaleInvoice').update({
// warehouseId: primaryWarehouse.id,
// });
// };
// }

View File

@@ -0,0 +1,30 @@
// import { Service, Inject } from 'typedi';
// import { IWarehouse } from '@/interfaces';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// @Service()
// export class ReceiptActivateWarehouses {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Updates all sale receipts transactions with the primary warehouse.
// * @param {number} tenantId
// * @param {number} primaryWarehouse
// * @returns {Promise<void>}
// */
// public updateReceiptsWithWarehouse = async (
// tenantId: number,
// primaryWarehouse: IWarehouse
// ): Promise<void> => {
// const { SaleReceipt, ItemEntry } = this.tenancy.models(tenantId);
// // Updates the vendor credits transactions with primary warehouse.
// await SaleReceipt.query().update({ warehouseId: primaryWarehouse.id });
// // Update the sale invoices entries with primary warehouse.
// await ItemEntry.query().where('referenceType', 'SaleReceipt').update({
// warehouseId: primaryWarehouse.id,
// });
// };
// }

View File

@@ -0,0 +1,30 @@
// import { Service, Inject } from 'typedi';
// import { IWarehouse } from '@/interfaces';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// @Service()
// export class VendorCreditActivateWarehouses {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Updates all vendor credits transactions with the primary warehouse.
// * @param {number} tenantId
// * @param {number} primaryWarehouse
// * @returns {Promise<void>}
// */
// public updateCreditsWithWarehouse = async (
// tenantId: number,
// primaryWarehouse: IWarehouse
// ): Promise<void> => {
// const { VendorCredit, ItemEntry } = this.tenancy.models(tenantId);
// // Updates the vendor credits transactions with primary warehouse.
// await VendorCredit.query().update({ warehouseId: primaryWarehouse.id });
// // Update the sale invoices entries with primary warehouse.
// await ItemEntry.query().where('referenceType', 'VendorCredit').update({
// warehouseId: primaryWarehouse.id,
// });
// };
// }

View File

@@ -0,0 +1,58 @@
// import { Service, Inject } from 'typedi';
// import events from '@/subscribers/events';
// import { IWarehousesActivatedPayload } from '@/interfaces';
// import { UpdateInventoryTransactionsWithWarehouse } from './UpdateInventoryTransactionsWithWarehouse';
// import { CreateInitialWarehousesItemsQuantity } from './CreateInitialWarehousesitemsQuantity';
// @Service()
// export class ActivateWarehousesSubscriber {
// @Inject()
// private updateInventoryTransactionsWithWarehouse: UpdateInventoryTransactionsWithWarehouse;
// @Inject()
// private createInitialWarehousesItemsQuantity: CreateInitialWarehousesItemsQuantity;
// /**
// * Attaches events with handlers.
// */
// attach(bus) {
// bus.subscribe(
// events.warehouse.onActivated,
// this.updateInventoryTransactionsWithWarehouseOnActivating
// );
// bus.subscribe(
// events.warehouse.onActivated,
// this.createInitialWarehousesItemsQuantityOnActivating
// );
// return bus;
// }
// /**
// * Updates inventory transactiont to primary warehouse once
// * multi-warehouses activated.
// * @param {IWarehousesActivatedPayload}
// */
// private updateInventoryTransactionsWithWarehouseOnActivating = async ({
// tenantId,
// primaryWarehouse,
// }: IWarehousesActivatedPayload) => {
// await this.updateInventoryTransactionsWithWarehouse.run(
// tenantId,
// primaryWarehouse.id
// );
// };
// /**
// * Creates initial warehouses items quantity once the multi-warehouses activated.
// * @param {IWarehousesActivatedPayload}
// */
// private createInitialWarehousesItemsQuantityOnActivating = async ({
// tenantId,
// primaryWarehouse,
// }: IWarehousesActivatedPayload) => {
// await this.createInitialWarehousesItemsQuantity.run(
// tenantId,
// primaryWarehouse.id
// );
// };
// }

View File

@@ -0,0 +1,26 @@
// import { Inject, Service } from 'typedi';
// import { ServiceError } from '@/exceptions';
// import { ERRORS } from './contants';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// export class CRUDWarehouse {
// @Inject()
// tenancy: HasTenancyService;
// getWarehouseOrThrowNotFound = async (tenantId: number, warehouseId: number) => {
// const { Warehouse } = this.tenancy.models(tenantId);
// const foundWarehouse = await Warehouse.query().findById(warehouseId);
// if (!foundWarehouse) {
// throw new ServiceError(ERRORS.WAREHOUSE_NOT_FOUND);
// }
// return foundWarehouse;
// };
// throwIfWarehouseNotFound = (warehouse) => {
// if (!warehouse) {
// throw new ServiceError(ERRORS.WAREHOUSE_NOT_FOUND);
// }
// }
// }

View File

@@ -0,0 +1,57 @@
// import { Service, Inject } from 'typedi';
// import { Knex } from 'knex';
// import { IItem, IItemWarehouseQuantityChange } from '@/interfaces';
// import { WarehousesItemsQuantitySync } from './Integrations/WarehousesItemsQuantitySync';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// @Service()
// export class CreateInitialWarehousesItemsQuantity {
// @Inject()
// private warehousesItemsQuantitySync: WarehousesItemsQuantitySync;
// @Inject()
// private tenancy: HasTenancyService;
// /**
// * Retrieves items warehouses quantity changes of the given inventory items.
// * @param {IItem[]} items
// * @param {IWarehouse} primaryWarehouse
// * @returns {IItemWarehouseQuantityChange[]}
// */
// private getWarehousesItemsChanges = (
// items: IItem[],
// primaryWarehouseId: number
// ): IItemWarehouseQuantityChange[] => {
// return items
// .filter((item: IItem) => item.quantityOnHand)
// .map((item: IItem) => ({
// itemId: item.id,
// warehouseId: primaryWarehouseId,
// amount: item.quantityOnHand,
// }));
// };
// /**
// * Creates initial warehouses items quantity.
// * @param {number} tenantId
// */
// public run = async (
// tenantId: number,
// primaryWarehouseId: number,
// trx?: Knex.Transaction
// ): Promise<void> => {
// const { Item } = this.tenancy.models(tenantId);
// const items = await Item.query(trx).where('type', 'Inventory');
// const warehousesChanges = this.getWarehousesItemsChanges(
// items,
// primaryWarehouseId
// );
// await this.warehousesItemsQuantitySync.mutateWarehousesItemsQuantity(
// tenantId,
// warehousesChanges,
// trx
// );
// };
// }

View File

@@ -0,0 +1,39 @@
// import {
// BillsActivateWarehousesSubscriber,
// CreditsActivateWarehousesSubscriber,
// InvoicesActivateWarehousesSubscriber,
// ReceiptsActivateWarehousesSubscriber,
// EstimatesActivateWarehousesSubscriber,
// InventoryActivateWarehousesSubscriber,
// VendorCreditsActivateWarehousesSubscriber,
// } from './Subscribers/Activate';
// import {
// BillWarehousesValidateSubscriber,
// CreditNoteWarehousesValidateSubscriber,
// SaleReceiptWarehousesValidateSubscriber,
// SaleEstimateWarehousesValidateSubscriber,
// SaleInvoicesWarehousesValidateSubscriber,
// VendorCreditWarehousesValidateSubscriber,
// InventoryAdjustmentWarehouseValidatorSubscriber,
// } from './Subscribers/Validators';
// import { DeleteItemWarehousesQuantitySubscriber } from './Subscribers/DeleteItemWarehousesQuantitySubscriber';
// export default () => [
// BillsActivateWarehousesSubscriber,
// CreditsActivateWarehousesSubscriber,
// InvoicesActivateWarehousesSubscriber,
// ReceiptsActivateWarehousesSubscriber,,
// EstimatesActivateWarehousesSubscriber,
// InventoryActivateWarehousesSubscriber,
// VendorCreditsActivateWarehousesSubscriber,
// BillWarehousesValidateSubscriber,
// CreditNoteWarehousesValidateSubscriber,
// SaleReceiptWarehousesValidateSubscriber,
// SaleEstimateWarehousesValidateSubscriber,
// SaleInvoicesWarehousesValidateSubscriber,
// VendorCreditWarehousesValidateSubscriber,
// InventoryAdjustmentWarehouseValidatorSubscriber,
// DeleteItemWarehousesQuantitySubscriber,
// ];

View File

@@ -0,0 +1,79 @@
// import { Inject, Service } from 'typedi';
// import { chain, difference } from 'lodash';
// import { ServiceError } from '@/exceptions';
// import { ERRORS } from './constants';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// @Service()
// export class ValidateWarehouseExistance {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Validate transaction warehouse id existance.
// * @param transDTO
// * @param entries
// */
// public validateWarehouseIdExistance = (
// transDTO: { warehouseId?: number },
// entries: { warehouseId?: number }[] = []
// ) => {
// const notAssignedWarehouseEntries = entries.filter((e) => !e.warehouseId);
// if (notAssignedWarehouseEntries.length > 0 && !transDTO.warehouseId) {
// throw new ServiceError(ERRORS.WAREHOUSE_ID_NOT_FOUND);
// }
// if (entries.length === 0 && !transDTO.warehouseId) {
// throw new ServiceError(ERRORS.WAREHOUSE_ID_NOT_FOUND);
// }
// };
// /**
// * Validate warehouse existance.
// * @param {number} tenantId
// * @param {number} warehouseId
// */
// public validateWarehouseExistance = (
// tenantId: number,
// warehouseId: number
// ) => {
// const { Warehouse } = this.tenancy.models(tenantId);
// const warehouse = Warehouse.query().findById(warehouseId);
// if (!warehouse) {
// throw new ServiceError(ERRORS.WAREHOUSE_ID_NOT_FOUND);
// }
// };
// /**
// *
// * @param {number} tenantId
// * @param {{ warehouseId?: number }[]} entries
// */
// public validateItemEntriesWarehousesExistance = async (
// tenantId: number,
// entries: { warehouseId?: number }[]
// ) => {
// const { Warehouse } = this.tenancy.models(tenantId);
// const entriesWarehousesIds = chain(entries)
// .filter((e) => !!e.warehouseId)
// .map((e) => e.warehouseId)
// .uniq()
// .value();
// const warehouses = await Warehouse.query().whereIn(
// 'id',
// entriesWarehousesIds
// );
// const warehousesIds = warehouses.map((e) => e.id);
// const notFoundWarehousesIds = difference(
// entriesWarehousesIds,
// warehousesIds
// );
// if (notFoundWarehousesIds.length > 0) {
// throw new ServiceError(ERRORS.WAREHOUSE_ID_NOT_FOUND);
// }
// };
// }

View File

@@ -0,0 +1,38 @@
// import { Service, Inject } from 'typedi';
// import { omit } from 'lodash';
// import * as R from 'ramda';
// import { WarehousesSettings } from '../WarehousesSettings';
// @Service()
// export class WarehouseTransactionDTOTransform {
// @Inject()
// private warehousesSettings: WarehousesSettings;
// /**
// * Excludes DTO warehouse id when mutli-warehouses feature is inactive.
// * @param {number} tenantId
// * @returns {Promise<Omit<T, 'warehouseId'> | T>}
// */
// private excludeDTOWarehouseIdWhenInactive = <
// T extends { warehouseId?: number }
// >(
// tenantId: number,
// DTO: T
// ): Omit<T, 'warehouseId'> | T => {
// const isActive = this.warehousesSettings.isMultiWarehousesActive(tenantId);
// return !isActive ? omit(DTO, ['warehouseId']) : DTO;
// };
// /**
// *
// * @param {number} tenantId
// * @param {T} DTO -
// * @returns {Omit<T, 'warehouseId'> | T}
// */
// public transformDTO =
// <T extends { warehouseId?: number }>(tenantId: number) =>
// (DTO: T): Omit<T, 'warehouseId'> | T => {
// return this.excludeDTOWarehouseIdWhenInactive<T>(tenantId, DTO);
// };
// }

View File

@@ -0,0 +1,66 @@
// import { Service, Inject } from 'typedi';
// import { isEmpty } from 'lodash';
// import { ValidateWarehouseExistance } from './ValidateWarehouseExistance';
// import { WarehousesSettings } from '../WarehousesSettings';
// interface IWarehouseTransactionDTO {
// warehouseId?: number|null;
// entries?: { warehouseId?: number|null }[];
// }
// @Service()
// export class WarehousesDTOValidators {
// @Inject()
// private validateWarehouseExistanceService: ValidateWarehouseExistance;
// @Inject()
// private warehousesSettings: WarehousesSettings;
// /**
// * Validates the warehouse existance of sale invoice transaction.
// * @param {number} tenantId
// * @param {ISaleInvoiceCreateDTO | ISaleInvoiceEditDTO} saleInvoiceDTO
// */
// public validateDTOWarehouseExistance = async (
// tenantId: number,
// DTO: IWarehouseTransactionDTO
// ) => {
// // Validates the sale invoice warehouse id existance.
// this.validateWarehouseExistanceService.validateWarehouseIdExistance(
// DTO,
// DTO.entries
// );
// // Validate the sale invoice warehouse existance on the storage.
// if (DTO.warehouseId) {
// this.validateWarehouseExistanceService.validateWarehouseExistance(
// tenantId,
// DTO.warehouseId
// );
// }
// // Validate the sale invoice entries warehouses existance on the storage.
// if (!isEmpty(DTO.entries)) {
// await this.validateWarehouseExistanceService.validateItemEntriesWarehousesExistance(
// tenantId,
// DTO.entries
// );
// }
// };
// /**
// * Validate the warehouse existance of
// * @param {number} tenantId
// * @param {IWarehouseTransactionDTO} saleInvoiceDTO
// * @returns
// */
// public validateDTOWarehouseWhenActive = async (
// tenantId: number,
// DTO: IWarehouseTransactionDTO
// ): Promise<void> => {
// const isActive = this.warehousesSettings.isMultiWarehousesActive(tenantId);
// // Can't continue if the multi-warehouses feature is inactive.
// if (!isActive) return;
// return this.validateDTOWarehouseExistance(tenantId, DTO);
// };
// }

View File

@@ -0,0 +1,117 @@
// import {
// IInventoryTransaction,
// IItemWarehouseQuantityChange,
// } from '@/interfaces';
// import { set, get, chain, toPairs } from 'lodash';
// export class WarehousesItemsQuantity {
// balanceMap: { [warehouseId: number]: { [itemId: number]: number } } = {};
// /**
// *
// * @param {number} warehouseId
// * @param {number} itemId
// * @returns {number}
// */
// public get = (warehouseId: number, itemId: number): number => {
// return get(this.balanceMap, `${warehouseId}.${itemId}`, 0);
// };
// /**
// *
// * @param {number} warehouseId
// * @param {number} itemId
// * @param {number} amount
// * @returns {WarehousesItemsQuantity}
// */
// public set = (warehouseId: number, itemId: number, amount: number) => {
// if (!get(this.balanceMap, warehouseId)) {
// set(this.balanceMap, warehouseId, {});
// }
// set(this.balanceMap, `${warehouseId}.${itemId}`, amount);
// return this;
// };
// /**
// *
// * @param {number} warehouseId
// * @param {number} itemId
// * @param {number} amount
// * @returns {WarehousesItemsQuantity}
// */
// public increment = (warehouseId: number, itemId: number, amount: number) => {
// const oldAmount = this.get(warehouseId, itemId);
// return this.set(warehouseId, itemId, oldAmount + amount);
// };
// /**
// *
// * @param {number} warehouseId
// * @param {number} itemId
// * @param {number} amount
// * @returns {WarehousesItemsQuantity}
// */
// public decrement = (warehouseId: number, itemId: number, amount: number) => {
// const oldAmount = this.get(warehouseId, itemId);
// return this.set(warehouseId, itemId, oldAmount - amount);
// };
// /**
// *
// * @returns {WarehousesItemsQuantity}
// */
// public reverse = () => {
// const collection = this.toArray();
// collection.forEach((change) => {
// this.set(change.warehouseId, change.itemId, change.amount * -1);
// });
// return this;
// };
// /**
// *
// * @returns {IItemWarehouseQuantityChange[]}
// */
// public toArray = (): IItemWarehouseQuantityChange[] => {
// return chain(this.balanceMap)
// .toPairs()
// .map(([warehouseId, item]) => {
// const pairs = toPairs(item);
// return pairs.map(([itemId, amount]) => ({
// itemId: parseInt(itemId),
// warehouseId: parseInt(warehouseId),
// amount,
// }));
// })
// .flatten()
// .value();
// };
// /**
// *
// * @param {IInventoryTransaction[]} inventoryTransactions
// * @returns {WarehousesItemsQuantity}
// */
// static fromInventoryTransaction = (
// inventoryTransactions: IInventoryTransaction[]
// ): WarehousesItemsQuantity => {
// const warehouseTransactions = inventoryTransactions.filter(
// (transaction) => transaction.warehouseId
// );
// const warehouseItemsQuantity = new WarehousesItemsQuantity();
// warehouseTransactions.forEach((transaction: IInventoryTransaction) => {
// const change =
// transaction.direction === 'IN'
// ? warehouseItemsQuantity.increment
// : warehouseItemsQuantity.decrement;
// change(transaction.warehouseId, transaction.itemId, transaction.quantity);
// });
// return warehouseItemsQuantity;
// };
// }

View File

@@ -0,0 +1,74 @@
// import events from '@/subscribers/events';
// import { Service, Inject } from 'typedi';
// import { WarehousesItemsQuantitySync } from './WarehousesItemsQuantitySync';
// import {
// IInventoryTransactionsCreatedPayload,
// IInventoryTransactionsDeletedPayload,
// } from '@/interfaces';
// import { WarehousesSettings } from '../WarehousesSettings';
// @Service()
// export class WarehousesItemsQuantitySyncSubscriber {
// @Inject()
// private warehousesItemsQuantitySync: WarehousesItemsQuantitySync;
// @Inject()
// private warehousesSettings: WarehousesSettings;
// /**
// * Attaches events with handlers.
// */
// public attach(bus) {
// bus.subscribe(
// events.inventory.onInventoryTransactionsCreated,
// this.syncWarehousesItemsQuantityOnInventoryTransCreated
// );
// bus.subscribe(
// events.inventory.onInventoryTransactionsDeleted,
// this.syncWarehousesItemsQuantityOnInventoryTransDeleted
// );
// return bus;
// }
// /**
// * Syncs warehouses items quantity once inventory transactions created.
// * @param {IInventoryTransactionsCreatedPayload}
// */
// private syncWarehousesItemsQuantityOnInventoryTransCreated = async ({
// tenantId,
// inventoryTransactions,
// trx,
// }: IInventoryTransactionsCreatedPayload) => {
// const isActive = this.warehousesSettings.isMultiWarehousesActive(tenantId);
// // Can't continue if the warehouses features is not active.
// if (!isActive) return;
// await this.warehousesItemsQuantitySync.mutateWarehousesItemsQuantityFromTransactions(
// tenantId,
// inventoryTransactions,
// trx
// );
// };
// /**
// * Syncs warehouses items quantity once inventory transactions deleted.
// * @param {IInventoryTransactionsDeletedPayload}
// */
// private syncWarehousesItemsQuantityOnInventoryTransDeleted = async ({
// tenantId,
// oldInventoryTransactions,
// trx,
// }: IInventoryTransactionsDeletedPayload) => {
// const isActive = this.warehousesSettings.isMultiWarehousesActive(tenantId);
// // Can't continue if the warehouses feature is not active yet.
// if (!isActive) return;
// await this.warehousesItemsQuantitySync.reverseWarehousesItemsQuantityFromTransactions(
// tenantId,
// oldInventoryTransactions,
// trx
// );
// };
// }

View File

@@ -0,0 +1,131 @@
// import { Knex } from 'knex';
// import { Service, Inject } from 'typedi';
// import { omit } from 'lodash';
// import {
// IInventoryTransaction,
// IItemWarehouseQuantityChange,
// } from '@/interfaces';
// import { WarehousesItemsQuantity } from './WarehousesItemsQuantity';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// @Service()
// export class WarehousesItemsQuantitySync {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Retrieves the reversed warehouses items quantity changes.
// * @param {IInventoryTransaction[]} inventoryTransactions
// * @returns {IItemWarehouseQuantityChange[]}
// */
// public getReverseWarehousesItemsQuantityChanges = (
// inventoryTransactions: IInventoryTransaction[]
// ): IItemWarehouseQuantityChange[] => {
// const warehouseItemsQuantity =
// WarehousesItemsQuantity.fromInventoryTransaction(inventoryTransactions);
// return warehouseItemsQuantity.reverse().toArray();
// };
// /**
// * Retrieves the warehouses items changes from the given inventory tranasctions.
// * @param {IInventoryTransaction[]} inventoryTransactions
// * @returns {IItemWarehouseQuantityChange[]}
// */
// public getWarehousesItemsQuantityChange = (
// inventoryTransactions: IInventoryTransaction[]
// ): IItemWarehouseQuantityChange[] => {
// const warehouseItemsQuantity =
// WarehousesItemsQuantity.fromInventoryTransaction(inventoryTransactions);
// return warehouseItemsQuantity.toArray();
// };
// /**
// * Mutates warehouses items quantity on hand on the storage.
// * @param {number} tenantId
// * @param {IItemWarehouseQuantityChange[]} warehousesItemsQuantity
// * @param {Knex.Transaction} trx
// */
// public mutateWarehousesItemsQuantity = async (
// tenantId: number,
// warehousesItemsQuantity: IItemWarehouseQuantityChange[],
// trx?: Knex.Transaction
// ): Promise<void> => {
// const mutationsOpers = warehousesItemsQuantity.map(
// (change: IItemWarehouseQuantityChange) =>
// this.mutateWarehouseItemQuantity(tenantId, change, trx)
// );
// await Promise.all(mutationsOpers);
// };
// /**
// * Mutates the warehouse item quantity.
// * @param {number} tenantId
// * @param {number} warehouseItemQuantity
// * @param {Knex.Transaction} trx
// */
// public mutateWarehouseItemQuantity = async (
// tenantId: number,
// warehouseItemQuantity: IItemWarehouseQuantityChange,
// trx: Knex.Transaction
// ): Promise<void> => {
// const { ItemWarehouseQuantity } = this.tenancy.models(tenantId);
// const itemWarehouseQuantity = await ItemWarehouseQuantity.query(trx)
// .where('itemId', warehouseItemQuantity.itemId)
// .where('warehouseId', warehouseItemQuantity.warehouseId)
// .first();
// if (itemWarehouseQuantity) {
// await ItemWarehouseQuantity.changeAmount(
// {
// itemId: warehouseItemQuantity.itemId,
// warehouseId: warehouseItemQuantity.warehouseId,
// },
// 'quantityOnHand',
// warehouseItemQuantity.amount,
// trx
// );
// } else {
// await ItemWarehouseQuantity.query(trx).insert({
// ...omit(warehouseItemQuantity, ['amount']),
// quantityOnHand: warehouseItemQuantity.amount,
// });
// }
// };
// /**
// * Mutates warehouses items quantity from inventory transactions.
// * @param {number} tenantId -
// * @param {IInventoryTransaction[]} inventoryTransactions -
// * @param {Knex.Transaction}
// */
// public mutateWarehousesItemsQuantityFromTransactions = async (
// tenantId: number,
// inventoryTransactions: IInventoryTransaction[],
// trx?: Knex.Transaction
// ) => {
// const changes = this.getWarehousesItemsQuantityChange(
// inventoryTransactions
// );
// await this.mutateWarehousesItemsQuantity(tenantId, changes, trx);
// };
// /**
// * Reverses warehouses items quantity from inventory transactions.
// * @param {number} tenantId
// * @param {IInventoryTransaction[]} inventoryTransactions
// * @param {Knex.Transaction} trx
// */
// public reverseWarehousesItemsQuantityFromTransactions = async (
// tenantId: number,
// inventoryTransactions: IInventoryTransaction[],
// trx?: Knex.Transaction
// ) => {
// const changes = this.getReverseWarehousesItemsQuantityChanges(
// inventoryTransactions
// );
// await this.mutateWarehousesItemsQuantity(tenantId, changes, trx);
// };
// }

View File

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

View File

@@ -0,0 +1,41 @@
import { GetItemWarehouseTransformer } from './GettItemWarehouseTransformer';
import { Inject, Injectable } from '@nestjs/common';
import { ItemWarehouseQuantity } from '../models/ItemWarehouseQuantity';
import { Item } from '@/modules/Items/models/Item';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
@Injectable()
export class GetItemWarehouses {
constructor(
@Inject(ItemWarehouseQuantity.name)
private readonly itemWarehouseQuantityModel: typeof ItemWarehouseQuantity,
@Inject(Item.name)
private readonly itemModel: typeof Item,
private readonly transformer: TransformerInjectable,
) {}
/**
* Retrieves the item warehouses.
* @param {number} itemId
* @returns
*/
public getItemWarehouses = async (itemId: number) => {
// Retrieves specific item or throw not found service error.
const item = await this.itemModel
.query()
.findById(itemId)
.throwIfNotFound();
const itemWarehouses = await this.itemWarehouseQuantityModel
.query()
.where('itemId', itemId)
.withGraphFetched('warehouse');
// Retrieves the transformed items warehouses.
return this.transformer.transform(
itemWarehouses,
new GetItemWarehouseTransformer(),
);
};
}

View File

@@ -0,0 +1,46 @@
import { Transformer } from '@/modules/Transformer/Transformer';
import { Item } from '@/modules/Items/models/Item';
export class GetItemWarehouseTransformer extends Transformer {
/**
* Include these attributes to sale invoice object.
* @returns {Array}
*/
public includeAttributes = (): string[] => {
return [
'warehouseId',
'warehouseName',
'warehouseCode',
'quantityOnHandFormatted',
];
};
/**
* Exclude the warehouse attribute.
* @returns {Array}
*/
public excludeAttributes = (): string[] => {
return ['warehouse'];
};
/**
* Formatted sell price.
* @param item
* @returns {string}
*/
public quantityOnHandFormatted(item: Item): string {
return this.formatNumber(item.quantityOnHand, { money: false });
}
public warehouseCode(item: Item): string {
return item.warehouse.code;
}
public warehouseName(item: Item): string {
return item.warehouse.name;
}
public warehouseId(item: Item): number {
return item.warehouse.id;
}
}

View File

@@ -0,0 +1,21 @@
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { Service, Inject } from 'typedi';
// @Service()
// export class UpdateInventoryTransactionsWithWarehouse {
// @Inject()
// tenancy: HasTenancyService;
// /**
// * Updates all inventory transactions with primary warehouse.
// * @param {number} tenantId -
// * @param {number} warehouseId -
// */
// public run = async (tenantId: number, primaryWarehouseId: number) => {
// const { InventoryTransaction } = this.tenancy.models(tenantId);
// await InventoryTransaction.query().update({
// warehouseId: primaryWarehouseId,
// });
// };
// }

View File

@@ -0,0 +1,212 @@
import { Knex } from 'knex';
export interface IWarehouse {
id?: number;
}
export interface IWarehouseTransfer {
id?: number;
date: Date;
fromWarehouseId: number;
toWarehouseId: number;
reason?: string;
transactionNumber: string;
entries: IWarehouseTransferEntry[];
transferInitiatedAt?: Date;
transferDeliveredAt?: Date;
isInitiated?: boolean;
isTransferred?: boolean;
}
export interface IWarehouseTransferEntry {
id?: number;
index?: number;
itemId: number;
description: string;
quantity: number;
cost: number;
}
export interface ICreateWarehouseDTO {
name: string;
code: string;
city?: string;
country?: string;
address?: string;
primary?: boolean;
}
export interface IEditWarehouseDTO {
name: string;
code: string;
city: string;
country: string;
address: string;
}
export interface IWarehouseTransferEntryDTO {
index?: number;
itemId: number;
description: string;
quantity: number;
cost?: number;
}
export interface ICreateWarehouseTransferDTO {
fromWarehouseId: number;
toWarehouseId: number;
transactionNumber: string;
date: Date;
transferInitiated: boolean;
transferDelivered: boolean;
entries: IWarehouseTransferEntryDTO[];
}
export interface IEditWarehouseTransferDTO {
fromWarehouseId: number;
toWarehouseId: number;
transactionNumber: string;
date: Date;
entries: {
id?: number;
itemId: number;
description: string;
quantity: number;
}[];
}
export interface IWarehouseEditPayload {
tenantId: number;
warehouseId: number;
warehouseDTO: IEditWarehouseDTO;
trx: Knex.Transaction;
}
export interface IWarehouseEditedPayload {
tenantId: number;
warehouse: IWarehouse;
warehouseDTO: IEditWarehouseDTO;
trx: Knex.Transaction;
}
export interface IWarehouseDeletePayload {
// tenantId: number;
warehouseId: number;
trx: Knex.Transaction;
}
export interface IWarehouseDeletedPayload {
tenantId: number;
warehouseId: number;
trx: Knex.Transaction;
}
export interface IWarehouseCreatePayload {
// tenantId: number;
warehouseDTO: ICreateWarehouseDTO;
trx: Knex.Transaction;
}
export interface IWarehouseCreatedPayload {
// tenantId: number;
warehouse: IWarehouse;
warehouseDTO: ICreateWarehouseDTO;
trx: Knex.Transaction;
}
export interface IWarehouseTransferCreate {
trx: Knex.Transaction;
warehouseTransferDTO: ICreateWarehouseTransferDTO;
tenantId: number;
}
export interface IWarehouseTransferCreated {
trx: Knex.Transaction;
warehouseTransfer: IWarehouseTransfer;
warehouseTransferDTO: ICreateWarehouseTransferDTO;
// tenantId: number;
}
export interface IWarehouseTransferEditPayload {
// tenantId: number;
editWarehouseDTO: IEditWarehouseTransferDTO;
oldWarehouseTransfer: IWarehouseTransfer;
trx: Knex.Transaction;
}
export interface IWarehouseTransferEditedPayload {
// tenantId: number;
editWarehouseDTO: IEditWarehouseTransferDTO;
oldWarehouseTransfer: IWarehouseTransfer;
warehouseTransfer: IWarehouseTransfer;
trx: Knex.Transaction;
}
export interface IWarehouseTransferDeletePayload {
// tenantId: number;
oldWarehouseTransfer: IWarehouseTransfer;
trx: Knex.Transaction;
}
export interface IWarehouseTransferDeletedPayload {
// tenantId: number;
warehouseTransfer: IWarehouseTransfer;
oldWarehouseTransfer: IWarehouseTransfer;
trx: Knex.Transaction;
}
export interface IGetWarehousesTransfersFilterDTO {
page: number;
pageSize: number;
searchKeyword: string;
}
export interface IItemWarehouseQuantityChange {
itemId: number;
warehouseId: number;
amount: number;
}
export interface IWarehousesActivatePayload {
// tenantId: number;
}
export interface IWarehousesActivatedPayload {
// tenantId: number;
primaryWarehouse: IWarehouse;
}
export interface IWarehouseMarkAsPrimaryPayload {
// tenantId: number;
oldWarehouse: IWarehouse;
trx: Knex.Transaction;
}
export interface IWarehouseMarkedAsPrimaryPayload {
// tenantId: number;
oldWarehouse: IWarehouse;
markedWarehouse: IWarehouse;
trx: Knex.Transaction;
}
export interface IWarehouseTransferInitiatePayload {
// tenantId: number;
oldWarehouseTransfer: IWarehouseTransfer;
trx: Knex.Transaction;
}
export interface IWarehouseTransferInitiatedPayload {
// tenantId: number;
warehouseTransfer: IWarehouseTransfer;
oldWarehouseTransfer: IWarehouseTransfer;
trx: Knex.Transaction;
}
export interface IWarehouseTransferTransferingPayload {
// tenantId: number;
oldWarehouseTransfer: IWarehouseTransfer;
trx: Knex.Transaction;
}
export interface IWarehouseTransferTransferredPayload {
// tenantId: number;
warehouseTransfer: IWarehouseTransfer;
oldWarehouseTransfer: IWarehouseTransfer;
trx: Knex.Transaction;
}

View File

@@ -0,0 +1,111 @@
import {
ICreateWarehouseDTO,
IEditWarehouseDTO,
IWarehouse,
} from './Warehouse.types';
import { ActivateWarehousesService } from './commands/ActivateWarehouses';
import { CreateWarehouse } from './commands/CreateWarehouse.service';
import { DeleteWarehouseService } from './commands/DeleteWarehouse.service';
import { EditWarehouse } from './commands/EditWarehouse.service';
import { GetWarehouse } from './queries/GetWarehouse';
import { GetWarehouses } from './queries/GetWarehouses';
import { GetItemWarehouses } from './Items/GetItemWarehouses';
import { WarehouseMarkPrimary } from './commands/WarehouseMarkPrimary.service';
import { Injectable } from '@nestjs/common';
@Injectable()
export class WarehousesApplication {
constructor(
private createWarehouseService: CreateWarehouse,
private editWarehouseService: EditWarehouse,
private deleteWarehouseService: DeleteWarehouseService,
private getWarehouseService: GetWarehouse,
private getWarehousesService: GetWarehouses,
private activateWarehousesService: ActivateWarehousesService,
private markWarehousePrimaryService: WarehouseMarkPrimary,
private getItemWarehousesService: GetItemWarehouses,
) {}
/**
* Creates a new warehouse.
* @param {ICreateWarehouseDTO} createWarehouseDTO
* @returns {Promise<IWarehouse>}
*/
public createWarehouse = (createWarehouseDTO: ICreateWarehouseDTO) => {
return this.createWarehouseService.createWarehouse(createWarehouseDTO);
};
/**
* Edits the given warehouse.
* @param {number} tenantId
* @param {number} warehouseId
* @param {IEditWarehouseDTO} editWarehouseDTO
* @returns {Promise<void>}
*/
public editWarehouse = (
warehouseId: number,
editWarehouseDTO: IEditWarehouseDTO,
) => {
return this.editWarehouseService.editWarehouse(
warehouseId,
editWarehouseDTO,
);
};
/**
* Deletes the given warehouse.
* @param {number} tenantId
* @param {number} warehouseId
*/
public deleteWarehouse = (warehouseId: number) => {
return this.deleteWarehouseService.deleteWarehouse(warehouseId);
};
/**
* Retrieves the specific warehouse.
* @param {number} warehouseId
* @returns
*/
public getWarehouse = (warehouseId: number) => {
return this.getWarehouseService.getWarehouse(warehouseId);
};
/**
*
* @param {number} tenantId
* @returns
*/
public getWarehouses = () => {
return this.getWarehousesService.getWarehouses();
};
/**
* Activates the warehouses feature.
* @returns {Promise<void>}
*/
public activateWarehouses = () => {
return this.activateWarehousesService.activateWarehouses();
};
/**
* Mark the given warehouse as primary.
* @param {number} tenantId -
* @returns {Promise<IWarehouse>}
*/
public markWarehousePrimary = (
tenantId: number,
warehouseId: number,
): Promise<IWarehouse> => {
return this.markWarehousePrimaryService.markAsPrimary(warehouseId);
};
/**
* Retrieves the specific item warehouses quantity.
* @param {number} tenantId
* @param {number} itemId
* @returns
*/
public getItemWarehouses = (itemId: number): Promise<any> => {
return this.getItemWarehousesService.getItemWarehouses(itemId);
};
}

View File

@@ -0,0 +1,25 @@
import { Injectable } from '@nestjs/common';
@Injectable()
export class WarehousesSettings {
/**
* Marks multi-warehouses as activated.
*/
public markMutliwarehoussAsActivated = () => {
// const settings = this.tenancy.settings(tenantId);
// settings.set({ group: 'features', key: Features.WAREHOUSES, value: 1 });
};
/**
* Detarmines multi-warehouses is active.
* @param {number} tenantId
* @returns {boolean}
*/
public isMultiWarehousesActive = () => {
// const settings = this.tenancy.settings(tenantId);
// return settings.get({ group: 'features', key: Features.WAREHOUSES });
return true;
};
}

View File

@@ -0,0 +1,60 @@
import { Knex } from 'knex';
import { Injectable } from '@nestjs/common';
import { CreateInitialWarehouse } from './CreateInitialWarehouse.service';
import { WarehousesSettings } from '../WarehousesSettings';
import { ERRORS } from '../contants';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { ServiceError } from '@/modules/Items/ServiceError';
import { events } from '@/common/events/events';
@Injectable()
export class ActivateWarehousesService {
/**
* @param {UnitOfWork} uow - Unit of work.
* @param {EventEmitter2} eventEmitter - Event emitter.
* @param {CreateInitialWarehouse} createInitialWarehouse - Create initial warehouse service.
* @param {WarehousesSettings} settings - Warehouses settings.
*/
constructor(
private readonly uow: UnitOfWork,
private readonly eventEmitter: EventEmitter2,
private readonly createInitialWarehouse: CreateInitialWarehouse,
private readonly settings: WarehousesSettings,
) {}
/**
* Throws error if the multi-warehouses is already activated.
*/
private throwIfWarehousesActivated(isActivated: boolean): void {
if (isActivated) {
throw new ServiceError(ERRORS.MUTLI_WAREHOUSES_ALREADY_ACTIVATED);
}
}
/**
* Activates the multi-warehouses.
*
* - Creates a new warehouses and mark it as primary.
* - Seed warehouses items quantity.
* - Mutate inventory transactions with the primary warehouse.
*/
public async activateWarehouses(): Promise<void> {
const isActivated = this.settings.isMultiWarehousesActive();
this.throwIfWarehousesActivated(isActivated);
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
await this.eventEmitter.emitAsync(events.warehouse.onActivate, { trx });
const primaryWarehouse =
await this.createInitialWarehouse.createInitialWarehouse();
this.settings.markMutliwarehoussAsActivated();
await this.eventEmitter.emitAsync(events.warehouse.onActivated, {
primaryWarehouse,
trx,
});
});
}
}

View File

@@ -0,0 +1,27 @@
import { CreateWarehouse } from './CreateWarehouse.service';
import { Injectable } from '@nestjs/common';
import { I18nContext } from 'nestjs-i18n';
@Injectable()
export class CreateInitialWarehouse {
/**
* @param {CreateWarehouse} createWarehouse - Create warehouse service.
* @param {I18nContext} i18n - I18n context.
*/
constructor(
private readonly createWarehouse: CreateWarehouse,
private readonly i18n: I18nContext,
) {}
/**
* Creates a initial warehouse.
* @param {number} tenantId
*/
public createInitialWarehouse = async () => {
return this.createWarehouse.createWarehouse({
name: this.i18n.t('warehouses.primary_warehouse'),
code: '10001',
primary: true,
});
};
}

View File

@@ -0,0 +1,73 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import {
ICreateWarehouseDTO,
IWarehouseCreatedPayload,
IWarehouseCreatePayload,
} from '../Warehouse.types';
import { WarehouseValidator } from './WarehouseValidator.service';
import { Warehouse } from '../models/Warehouse.model';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
@Injectable()
export class CreateWarehouse {
/**
* @param {UnitOfWork} uow - Unit of work.
* @param {EventEmitter2} eventEmitter - Event emitter.
* @param {WarehouseValidator} validator - Warehouse command validator.
* @param {typeof Warehouse} warehouseModel - Warehouse model.
*/
constructor(
private readonly uow: UnitOfWork,
private readonly eventEmitter: EventEmitter2,
private readonly validator: WarehouseValidator,
@Inject(Warehouse.name)
private readonly warehouseModel: typeof Warehouse,
) {}
/**
* Authorize the warehouse before creating.
* @param {ICreateWarehouseDTO} warehouseDTO -
*/
public authorize = async (warehouseDTO: ICreateWarehouseDTO) => {
if (warehouseDTO.code) {
await this.validator.validateWarehouseCodeUnique(warehouseDTO.code);
}
};
/**
* Creates a new warehouse on the system.
* @param {ICreateWarehouseDTO} warehouseDTO
*/
public createWarehouse = async (
warehouseDTO: ICreateWarehouseDTO
): Promise<Warehouse> => {
// Authorize warehouse before creating.
await this.authorize(warehouseDTO);
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onWarehouseCreate` event.
await this.eventEmitter.emitAsync(events.warehouse.onEdit, {
warehouseDTO,
trx,
} as IWarehouseCreatePayload);
// Creates a new warehouse on the storage.
const warehouse = await this.warehouseModel.query(trx).insertAndFetch({
...warehouseDTO,
});
// Triggers `onWarehouseCreated` event.
await this.eventEmitter.emitAsync(events.warehouse.onCreated, {
warehouseDTO,
warehouse,
trx,
} as IWarehouseCreatedPayload);
return warehouse;
});
};
}

View File

@@ -0,0 +1,29 @@
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { ItemWarehouseQuantity } from '../models/ItemWarehouseQuantity';
@Injectable()
export class DeleteItemWarehousesQuantity {
/**
* @param {typeof ItemWarehouseQuantity} itemWarehouseQuantityModel - Item warehouse quantity model.
*/
constructor(
@Inject(ItemWarehouseQuantity.name)
private readonly itemWarehouseQuantityModel: typeof ItemWarehouseQuantity,
) {}
/**
* Deletes the given item warehouses quantities.
* @param {number} itemId
* @param {Knex.Transaction} trx -
*/
public deleteItemWarehousesQuantity = async (
itemId: number,
trx?: Knex.Transaction,
): Promise<void> => {
await this.itemWarehouseQuantityModel
.query(trx)
.where('itemId', itemId)
.delete();
};
}

Some files were not shown because too many files have changed in this diff Show More