add server to monorepo.

This commit is contained in:
a.bouhuolia
2023-02-03 11:57:50 +02:00
parent 28e309981b
commit 80b97b5fdc
1303 changed files with 137049 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
import { Service, Inject } from 'typedi';
import { ServiceError } from '@/exceptions';
import { ERRORS } from './constants';
import { Knex } from 'knex';
import events from '@/subscribers/events';
import {
IBranchesActivatedPayload,
IBranchesActivatePayload,
} from '@/interfaces';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import UnitOfWork from '@/services/UnitOfWork';
import { CreateBranch } from './CreateBranch';
import { BranchesSettings } from './BranchesSettings';
import HasTenancyService from '@/services/Tenancy/TenancyService';
@Service()
export class ActivateBranches {
@Inject()
private uow: UnitOfWork;
@Inject()
private tenancy: HasTenancyService;
@Inject()
private eventPublisher: EventPublisher;
@Inject()
private createBranch: CreateBranch;
@Inject()
private branchesSettings: BranchesSettings;
/**
* Throws service error if multi-branches feature is already activated.
* @param {boolean} isActivated
*/
private throwIfMultiBranchesActivated = (isActivated: boolean) => {
if (isActivated) {
throw new ServiceError(ERRORS.MUTLI_BRANCHES_ALREADY_ACTIVATED);
}
};
/**
* Creates a new initial branch.
* @param {number} tenantId
*/
private createInitialBranch = (tenantId: number) => {
const { __ } = this.tenancy.i18n(tenantId);
return this.createBranch.createBranch(tenantId, {
name: __('branches.head_branch'),
code: '10001',
primary: true,
});
};
/**
* Activate multi-branches feature.
* @param {number} tenantId
* @returns {Promise<void>}
*/
public activateBranches = (tenantId: number): Promise<void> => {
const isActivated = this.branchesSettings.isMultiBranchesActive(tenantId);
// Throw error if mutli-branches is already activated.
this.throwIfMultiBranchesActivated(isActivated);
// Activate multi-branches under unit-of-work envirement.
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Triggers `onBranchActivate` branch.
await this.eventPublisher.emitAsync(events.branch.onActivate, {
tenantId,
trx,
} as IBranchesActivatePayload);
// Create a new branch as primary branch.
const primaryBranch = await this.createInitialBranch(tenantId);
// Mark the mutli-branches is activated.
await this.branchesSettings.markMultiBranchesAsActivated(tenantId);
// Triggers `onBranchActivated` branch.
await this.eventPublisher.emitAsync(events.branch.onActivated, {
tenantId,
primaryBranch,
trx,
} as IBranchesActivatedPayload);
});
};
}

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,52 @@
import { Inject, Service } from 'typedi';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { ServiceError } from '@/exceptions';
import { ERRORS } from './constants';
@Service()
export class BranchValidator {
@Inject()
tenancy: HasTenancyService;
public validateBranchNotOnlyWarehouse = async (
tenantId: number,
branchId: number
) => {
const { Branch } = this.tenancy.models(tenantId);
const warehouses = await Branch.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 {number} tenantId
* @param {string} code
* @param {number} exceptBranchId
*/
public validateBranchCodeUnique = async (
tenantId: number,
code: string,
exceptBranchId?: number
): Promise<void> => {
const { Branch } = this.tenancy.models(tenantId);
const branch = await Branch.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,112 @@
import { IBranch, ICreateBranchDTO, IEditBranchDTO } from '@/interfaces';
import { Service, Inject } from 'typedi';
import { ActivateBranches } from './ActivateBranches';
import { CreateBranch } from './CreateBranch';
import { DeleteBranch } from './DeleteBranch';
import { EditBranch } from './EditBranch';
import { GetBranch } from './GetBranch';
import { GetBranches } from './GetBranches';
import { MarkBranchAsPrimary } from './MarkBranchAsPrimary';
@Service()
export class BranchesApplication {
@Inject()
private deleteBranchService: DeleteBranch;
@Inject()
private createBranchService: CreateBranch;
@Inject()
private getBranchService: GetBranch;
@Inject()
private editBranchService: EditBranch;
@Inject()
private getBranchesService: GetBranches;
@Inject()
private activateBranchesService: ActivateBranches;
@Inject()
private markBranchAsPrimaryService: MarkBranchAsPrimary;
/**
* Retrieves branches list.
* @param {number} tenantId
* @returns {IBranch}
*/
public getBranches = (tenantId: number): Promise<IBranch[]> => {
return this.getBranchesService.getBranches(tenantId);
};
/**
* Retrieves the given branch details.
* @param {number} tenantId - Tenant id.
* @param {number} branchId - Branch id.
* @returns {Promise<IBranch>}
*/
public getBranch = (tenantId: number, branchId: number): Promise<IBranch> => {
return this.getBranchService.getBranch(tenantId, branchId);
};
/**
* Creates a new branch.
* @param {number} tenantId -
* @param {ICreateBranchDTO} createBranchDTO
* @returns {Promise<IBranch>}
*/
public createBranch = (
tenantId: number,
createBranchDTO: ICreateBranchDTO
): Promise<IBranch> => {
return this.createBranchService.createBranch(tenantId, createBranchDTO);
};
/**
* Edits the given branch.
* @param {number} tenantId - Tenant id.
* @param {number} branchId - Branch id.
* @param {IEditBranchDTO} editBranchDTO - Edit branch DTO.
* @returns {Promise<IBranch>}
*/
public editBranch = (
tenantId: number,
branchId: number,
editBranchDTO: IEditBranchDTO
): Promise<IBranch> => {
return this.editBranchService.editBranch(tenantId, branchId, editBranchDTO);
};
/**
* Deletes the given branch.
* @param {number} tenantId - Tenant id.
* @param {number} branchId - Branch id.
* @returns {Promise<void>}
*/
public deleteBranch = (tenantId: number, branchId: number): Promise<void> => {
return this.deleteBranchService.deleteBranch(tenantId, branchId);
};
/**
* Activates the given branches.
* @param {number} tenantId - Tenant id.
* @returns {Promise<void>}
*/
public activateBranches = (tenantId: number): Promise<void> => {
return this.activateBranchesService.activateBranches(tenantId);
};
/**
* Marks the given branch as primary.
* @param {number} tenantId
* @param {number} branchId
* @returns {Promise<IBranch>}
*/
public markBranchAsPrimary = async (
tenantId: number,
branchId: number
): Promise<IBranch> => {
return this.markBranchAsPrimaryService.markAsPrimary(tenantId, 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,64 @@
import { Service, Inject } from 'typedi';
import { Knex } from 'knex';
import {
IBranch,
IBranchCreatedPayload,
IBranchCreatePayload,
ICreateBranchDTO,
} from '@/interfaces';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import UnitOfWork from '@/services/UnitOfWork';
import events from '@/subscribers/events';
import { BranchValidator } from './BranchValidate';
@Service()
export class CreateBranch {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private uow: UnitOfWork;
@Inject()
private eventPublisher: EventPublisher;
@Inject()
private validator: BranchValidator;
/**
* Creates a new branch.
* @param {number} tenantId
* @param {ICreateBranchDTO} createBranchDTO
* @returns {Promise<IBranch>}
*/
public createBranch = (
tenantId: number,
createBranchDTO: ICreateBranchDTO
): Promise<IBranch> => {
const { Branch } = this.tenancy.models(tenantId);
// Creates a new branch under unit-of-work.
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Triggers `onBranchCreate` event.
await this.eventPublisher.emitAsync(events.warehouse.onEdit, {
tenantId,
createBranchDTO,
trx,
} as IBranchCreatePayload);
const branch = await Branch.query().insertAndFetch({
...createBranchDTO,
});
// Triggers `onBranchCreated` event.
await this.eventPublisher.emitAsync(events.warehouse.onEdited, {
tenantId,
createBranchDTO,
branch,
trx,
} as IBranchCreatedPayload);
return branch;
});
};
}

View File

@@ -0,0 +1,76 @@
import { Service, Inject } from 'typedi';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import UnitOfWork from '@/services/UnitOfWork';
import { Knex } from 'knex';
import events from '@/subscribers/events';
import { IBranchDeletedPayload, IBranchDeletePayload } from '@/interfaces';
import { CURDBranch } from './CRUDBranch';
import { BranchValidator } from './BranchValidate';
import { ERRORS } from './constants';
@Service()
export class DeleteBranch extends CURDBranch {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private uow: UnitOfWork;
@Inject()
private eventPublisher: EventPublisher;
@Inject()
private validator: BranchValidator;
/**
* Validates the branch deleteing.
* @param {number} tenantId
* @param {number} branchId
* @returns {Promise<void>}
*/
private authorize = async (tenantId: number, branchId: number) => {
await this.validator.validateBranchNotOnlyWarehouse(tenantId, branchId);
};
/**
* Deletes branch.
* @param {number} tenantId
* @param {number} branchId
* @returns {Promise<void>}
*/
public deleteBranch = async (
tenantId: number,
branchId: number
): Promise<void> => {
const { Branch } = this.tenancy.models(tenantId);
// Retrieves the old branch or throw not found service error.
const oldBranch = await Branch.query()
.findById(branchId)
.throwIfNotFound()
.queryAndThrowIfHasRelations({
type: ERRORS.BRANCH_HAS_ASSOCIATED_TRANSACTIONS,
});
// Authorize the branch before deleting.
await this.authorize(tenantId, branchId);
// Deletes branch under unit-of-work.
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Triggers `onBranchCreate` event.
await this.eventPublisher.emitAsync(events.warehouse.onEdit, {
tenantId,
oldBranch,
trx,
} as IBranchDeletePayload);
await Branch.query().findById(branchId).delete();
// Triggers `onBranchCreate` event.
await this.eventPublisher.emitAsync(events.warehouse.onEdited, {
tenantId,
oldBranch,
trx,
} as IBranchDeletedPayload);
});
};
}

View File

@@ -0,0 +1,65 @@
import { Service, Inject } from 'typedi';
import { Knex } from 'knex';
import {
IBranchEditedPayload,
IBranchEditPayload,
IEditBranchDTO,
} from '@/interfaces';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import UnitOfWork from '@/services/UnitOfWork';
import { CURDBranch } from './CRUDBranch';
import events from '@/subscribers/events';
@Service()
export class EditBranch extends CURDBranch {
@Inject()
tenancy: HasTenancyService;
@Inject()
uow: UnitOfWork;
@Inject()
eventPublisher: EventPublisher;
/**
* Edits branch.
* @param {number} tenantId
* @param {number} branchId
* @param editBranchDTO
*/
public editBranch = async (
tenantId: number,
branchId: number,
editBranchDTO: IEditBranchDTO
) => {
const { Branch } = this.tenancy.models(tenantId);
// Retrieves the old branch or throw not found service error.
const oldBranch = await this.getBranchOrThrowNotFound(tenantId, branchId);
// Deletes branch under unit-of-work.
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Triggers `onBranchEdit` event.
await this.eventPublisher.emitAsync(events.warehouse.onEdit, {
tenantId,
oldBranch,
trx,
} as IBranchEditPayload);
// Edits the branch on the storage.
const branch = await Branch.query().patchAndFetchById(branchId, {
...editBranchDTO,
});
// Triggers `onBranchEdited` event.
await this.eventPublisher.emitAsync(events.warehouse.onEdited, {
tenantId,
oldBranch,
branch,
trx,
} as IBranchEditedPayload);
return branch;
});
};
}

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,26 @@
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { Service, Inject } from 'typedi';
import { CURDBranch } from './CRUDBranch';
@Service()
export class GetBranch extends CURDBranch{
@Inject()
tenancy: HasTenancyService;
/**
*
* @param {number} tenantId
* @param {number} branchId
* @returns
*/
public getBranch = async (tenantId: number, branchId: number) => {
const { Branch } = this.tenancy.models(tenantId);
const branch = await Branch.query().findById(branchId);
// Throw not found service error if the branch not found.
this.throwIfBranchNotFound(branch);
return branch;
};
}

View File

@@ -0,0 +1,22 @@
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { Service, Inject } from 'typedi';
@Service()
export class GetBranches {
@Inject()
tenancy: HasTenancyService;
/**
* Retrieves branches list.
* @param {number} tenantId
* @param {number} branchId
* @returns
*/
public getBranches = async (tenantId: number) => {
const { Branch } = this.tenancy.models(tenantId);
const branches = await Branch.query().orderBy('name', 'DESC');
return branches;
};
}

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,74 @@
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,67 @@
import { Service, Inject } from 'typedi';
import { Knex } from 'knex';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import UnitOfWork from '@/services/UnitOfWork';
import events from '@/subscribers/events';
import { CURDBranch } from './CRUDBranch';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import {
IBranch,
IBranchMarkAsPrimaryPayload,
IBranchMarkedAsPrimaryPayload,
} from '@/interfaces';
@Service()
export class MarkBranchAsPrimary extends CURDBranch {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private uow: UnitOfWork;
@Inject()
private eventPublisher: EventPublisher;
/**
* Marks the given branch as primary.
* @param {number} tenantId
* @param {number} branchId
* @returns {Promise<IBranch>}
*/
public markAsPrimary = async (
tenantId: number,
branchId: number
): Promise<IBranch> => {
const { Branch } = this.tenancy.models(tenantId);
// Retrieves the old branch or throw not found service error.
const oldBranch = await this.getBranchOrThrowNotFound(tenantId, branchId);
// Updates the branches under unit-of-work envirement.
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Triggers `onBranchMarkPrimary` event.
await this.eventPublisher.emitAsync(events.branch.onMarkPrimary, {
tenantId,
oldBranch,
trx,
} as IBranchMarkAsPrimaryPayload);
// Updates all branches as not primary.
await Branch.query(trx).update({ primary: false });
// Updates the given branch as primary.
const markedBranch = await Branch.query(trx).patchAndFetchById(branchId, {
primary: true,
});
// Triggers `onBranchMarkedPrimary` event.
await this.eventPublisher.emitAsync(events.branch.onMarkedPrimary, {
tenantId,
markedBranch,
oldBranch,
trx,
} as IBranchMarkedAsPrimaryPayload);
return markedBranch;
});
};
}

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 './CashflowBranchesActviateSubscriber';
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 {
IPaymentReceiveCreatingPayload,
IPaymentReceiveEditingPayload,
} 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 {IPaymentReceiveCreatingPayload} payload
*/
private validateBranchExistanceOnPaymentCreating = async ({
tenantId,
paymentReceiveDTO,
}: IPaymentReceiveCreatingPayload) => {
await this.validateBranchExistance.validateTransactionBranchWhenActive(
tenantId,
paymentReceiveDTO.branchId
);
};
/**
* Validate branch existance once estimate editing.
* @param {IPaymentReceiveEditingPayload} payload
*/
private validateBranchExistanceOnPaymentEditing = async ({
paymentReceiveDTO,
tenantId,
}: IPaymentReceiveEditingPayload) => {
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

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