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,128 @@
import { Service, Inject } from 'typedi';
import { Knex } from 'knex';
import * as R from 'ramda';
import {
IRefundVendorCredit,
IRefundVendorCreditCreatedPayload,
IRefundVendorCreditCreatingPayload,
IRefundVendorCreditDTO,
IVendorCredit,
IVendorCreditCreatePayload,
} from '@/interfaces';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import UnitOfWork from '@/services/UnitOfWork';
import RefundVendorCredit from './RefundVendorCredit';
import events from '@/subscribers/events';
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
@Service()
export default class CreateRefundVendorCredit extends RefundVendorCredit {
@Inject()
tenancy: HasTenancyService;
@Inject()
uow: UnitOfWork;
@Inject()
eventPublisher: EventPublisher;
@Inject()
private branchDTOTransform: BranchTransactionDTOTransform;
/**
* Creates a refund vendor credit.
* @param {number} tenantId
* @param {number} vendorCreditId
* @param {IRefundVendorCreditDTO} refundVendorCreditDTO
* @returns {Promise<IRefundVendorCredit>}
*/
public createRefund = async (
tenantId: number,
vendorCreditId: number,
refundVendorCreditDTO: IRefundVendorCreditDTO
): Promise<IRefundVendorCredit> => {
const { RefundVendorCredit, Account, VendorCredit } =
this.tenancy.models(tenantId);
// Retrieve the vendor credit or throw not found service error.
const vendorCredit = await VendorCredit.query()
.findById(vendorCreditId)
.throwIfNotFound();
// Retrieve the deposit account or throw not found service error.
const depositAccount = await Account.query()
.findById(refundVendorCreditDTO.depositAccountId)
.throwIfNotFound();
// Validate vendor credit has remaining credit.
this.validateVendorCreditRemainingCredit(
vendorCredit,
refundVendorCreditDTO.amount
);
// Validate refund deposit account type.
this.validateRefundDepositAccountType(depositAccount);
// Triggers `onVendorCreditRefundCreate` event.
await this.eventPublisher.emitAsync(events.vendorCredit.onRefundCreate, {
tenantId,
vendorCreditId,
refundVendorCreditDTO,
} as IVendorCreditCreatePayload);
const refundCreditObj = this.transformDTOToModel(
tenantId,
vendorCredit,
refundVendorCreditDTO
);
// Saves refund vendor credit with associated transactions.
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
const eventPayload = {
vendorCredit,
trx,
tenantId,
refundVendorCreditDTO,
} as IRefundVendorCreditCreatingPayload;
// Triggers `onVendorCreditRefundCreating` event.
await this.eventPublisher.emitAsync(
events.vendorCredit.onRefundCreating,
eventPayload as IRefundVendorCreditCreatingPayload
);
// Inserts refund vendor credit to the storage layer.
const refundVendorCredit =
await RefundVendorCredit.query().insertAndFetch({
...refundCreditObj,
});
// Triggers `onVendorCreditCreated` event.
await this.eventPublisher.emitAsync(events.vendorCredit.onRefundCreated, {
...eventPayload,
refundVendorCredit,
} as IRefundVendorCreditCreatedPayload);
return refundVendorCredit;
});
};
/**
* Transformes the refund DTO to refund vendor credit model.
* @param {IVendorCredit} vendorCredit -
* @param {IRefundVendorCreditDTO} vendorCreditDTO
* @returns {IRefundVendorCredit}
*/
public transformDTOToModel = (
tenantId: number,
vendorCredit: IVendorCredit,
vendorCreditDTO: IRefundVendorCreditDTO
) => {
const initialDTO = {
vendorCreditId: vendorCredit.id,
...vendorCreditDTO,
currencyCode: vendorCredit.currencyCode,
exchangeRate: vendorCreditDTO.exchangeRate || 1,
};
return R.compose(this.branchDTOTransform.transformDTO(tenantId))(
initialDTO
);
};
}

View File

@@ -0,0 +1,73 @@
import { Knex } from 'knex';
import {
IRefundVendorCreditDeletedPayload,
IRefundVendorCreditDeletePayload,
IRefundVendorCreditDeletingPayload,
} from '@/interfaces';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import UnitOfWork from '@/services/UnitOfWork';
import events from '@/subscribers/events';
import { Inject, Service } from 'typedi';
import RefundVendorCredit from './RefundVendorCredit';
@Service()
export default class DeleteRefundVendorCredit extends RefundVendorCredit {
@Inject()
tenancy: HasTenancyService;
@Inject()
uow: UnitOfWork;
@Inject()
eventPublisher: EventPublisher;
/**
* Retrieve the credit note graph.
* @param {number} tenantId - Tenant id.
* @param {number} creditNoteId - Credit note id.
* @returns {Promise<void>}
*/
public deleteRefundVendorCreditRefund = async (
tenantId: number,
refundCreditId: number
): Promise<void> => {
const { RefundVendorCredit } = this.tenancy.models(tenantId);
// Retrieve the old credit note or throw not found service error.
const oldRefundCredit = await this.getRefundVendorCreditOrThrowError(
tenantId,
refundCreditId
);
// Triggers `onVendorCreditRefundDelete` event.
await this.eventPublisher.emitAsync(events.vendorCredit.onRefundDelete, {
refundCreditId,
oldRefundCredit,
tenantId,
} as IRefundVendorCreditDeletePayload);
// Deletes the refund vendor credit under unit-of-work envirement.
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
const eventPayload = {
trx,
refundCreditId,
oldRefundCredit,
tenantId,
} as IRefundVendorCreditDeletingPayload;
// Triggers `onVendorCreditRefundDeleting` event.
await this.eventPublisher.emitAsync(
events.vendorCredit.onRefundDeleting,
eventPayload
);
// Deletes the refund vendor credit graph from the storage.
await RefundVendorCredit.query(trx).findById(refundCreditId).delete();
// Triggers `onVendorCreditRefundDeleted` event.
await this.eventPublisher.emitAsync(
events.vendorCredit.onRefundDeleted,
eventPayload as IRefundVendorCreditDeletedPayload
);
});
};
}

View File

@@ -0,0 +1,43 @@
import { Service, Inject } from 'typedi';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { RefundVendorCreditTransformer } from './RefundVendorCreditTransformer';
import RefundVendorCredit from './RefundVendorCredit';
import { IRefundVendorCredit } from '@/interfaces';
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
@Service()
export default class GetRefundVendorCredit extends RefundVendorCredit {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private transformer: TransformerInjectable;
/**
* Retrieve refund vendor credit transaction.
* @param {number} tenantId
* @param {number} refundId
* @returns {Promise<IRefundVendorCredit>}
*/
public getRefundCreditTransaction = async (
tenantId: number,
refundId: number
): Promise<IRefundVendorCredit> => {
const { RefundVendorCredit } = this.tenancy.models(tenantId);
await this.getRefundVendorCreditOrThrowError(tenantId, refundId);
// Retrieve refund transactions associated to the given vendor credit.
const refundVendorTransactions = await RefundVendorCredit.query()
.findById(refundId)
.withGraphFetched('vendorCredit')
.withGraphFetched('depositAccount');
// Transformes refund vendor credit models to POJO objects.
return this.transformer.transform(
tenantId,
refundVendorTransactions,
new RefundVendorCreditTransformer()
);
};
}

View File

@@ -0,0 +1,41 @@
import { IRefundVendorCreditPOJO } from '@/interfaces';
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { Inject, Service } from 'typedi';
import RefundVendorCredit from './RefundVendorCredit';
import { RefundVendorCreditTransformer } from './RefundVendorCreditTransformer';
@Service()
export default class ListVendorCreditRefunds extends RefundVendorCredit {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private transformer: TransformerInjectable;
/**
* Retrieve the credit note graph.
* @param {number} tenantId
* @param {number} creditNoteId
* @returns {Promise<IRefundCreditNotePOJO[]>}
*/
public getVendorCreditRefunds = async (
tenantId: number,
vendorCreditId: number
): Promise<IRefundVendorCreditPOJO[]> => {
const { RefundVendorCredit } = this.tenancy.models(tenantId);
// Retrieve refund transactions associated to the given vendor credit.
const refundVendorTransactions = await RefundVendorCredit.query()
.where('vendorCreditId', vendorCreditId)
.withGraphFetched('vendorCredit')
.withGraphFetched('depositAccount');
// Transformes refund vendor credit models to POJO objects.
return this.transformer.transform(
tenantId,
refundVendorTransactions,
new RefundVendorCreditTransformer()
);
};
}

View File

@@ -0,0 +1,47 @@
import Knex from 'knex';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { Inject, Service } from 'typedi';
@Service()
export default class RefundSyncCreditRefundedAmount {
@Inject()
tenancy: HasTenancyService;
/**
* Increment vendor credit refunded amount.
* @param {number} tenantId - Tenant id.
* @param {number} amount - Amount.
* @param {Knex.Transaction} trx - Knex transaction.
*/
public incrementCreditRefundedAmount = async (
tenantId: number,
vendorCreditId: number,
amount: number,
trx?: Knex.Transaction
): Promise<void> => {
const { VendorCredit } = this.tenancy.models(tenantId);
await VendorCredit.query(trx)
.findById(vendorCreditId)
.increment('refundedAmount', amount);
};
/**
* Decrement vendor credit refunded amount.
* @param {number} tenantId
* @param {number} amount
* @param {Knex.Transaction} trx
*/
public decrementCreditNoteRefundAmount = async (
tenantId: number,
vendorCreditId: number,
amount: number,
trx?: Knex.Transaction
): Promise<void> => {
const { VendorCredit } = this.tenancy.models(tenantId);
await VendorCredit.query(trx)
.findById(vendorCreditId)
.decrement('refundedAmount', amount);
};
}

View File

@@ -0,0 +1,48 @@
import Knex from 'knex';
import { IRefundVendorCredit } from '@/interfaces';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { Inject, Service } from 'typedi';
@Service()
export default class RefundSyncVendorCreditBalance {
@Inject()
tenancy: HasTenancyService;
/**
* Increment vendor credit refunded amount.
* @param {number} tenantId -
* @param {IRefundVendorCredit} refundCreditNote -
* @param {Knex.Transaction} trx -
*/
public incrementVendorCreditRefundAmount = async (
tenantId: number,
refundVendorCredit: IRefundVendorCredit,
trx?: Knex.Transaction
): Promise<void> => {
const { VendorCredit } = this.tenancy.models(tenantId);
await VendorCredit.query(trx).increment(
'refundedAmount',
refundVendorCredit.amount
);
};
/**
* Decrement vendor credit refunded amount.
* @param {number} tenantId
* @param {IRefundVendorCredit} refundCreditNote
* @param {Knex.Transaction} trx
*/
public decrementVendorCreditRefundAmount = async (
tenantId: number,
refundVendorCredit: IRefundVendorCredit,
trx?: Knex.Transaction
): Promise<void> => {
const { VendorCredit } = this.tenancy.models(tenantId);
await VendorCredit.query(trx).decrement(
'refundedAmount',
refundVendorCredit.amount
);
};
}

View File

@@ -0,0 +1,62 @@
import { Service, Inject } from 'typedi';
import {
IRefundVendorCreditCreatedPayload,
IRefundVendorCreditDeletedPayload,
} from '@/interfaces';
import events from '@/subscribers/events';
import RefundSyncCreditRefundedAmount from './RefundSyncCreditRefundedAmount';
@Service()
export default class RefundSyncVendorCreditBalanceSubscriber {
@Inject()
refundSyncCreditRefunded: RefundSyncCreditRefundedAmount;
/**
* Attaches events with handlers.
*/
public attach = (bus) => {
bus.subscribe(
events.vendorCredit.onRefundCreated,
this.incrementRefundedAmountOnceRefundCreated
);
bus.subscribe(
events.vendorCredit.onRefundDeleted,
this.decrementRefundedAmountOnceRefundDeleted
);
};
/**
* Increment refunded vendor credit amount once refund transaction created.
* @param {IRefundVendorCreditCreatedPayload} payload -
*/
private incrementRefundedAmountOnceRefundCreated = async ({
refundVendorCredit,
vendorCredit,
tenantId,
trx,
}: IRefundVendorCreditCreatedPayload) => {
await this.refundSyncCreditRefunded.incrementCreditRefundedAmount(
tenantId,
refundVendorCredit.vendorCreditId,
refundVendorCredit.amount,
trx
);
};
/**
* Decrement refunded vendor credit amount once refund transaction deleted.
* @param {IRefundVendorCreditDeletedPayload} payload -
*/
private decrementRefundedAmountOnceRefundDeleted = async ({
trx,
oldRefundCredit,
tenantId,
}: IRefundVendorCreditDeletedPayload) => {
await this.refundSyncCreditRefunded.decrementCreditNoteRefundAmount(
tenantId,
oldRefundCredit.vendorCreditId,
oldRefundCredit.amount,
trx
);
};
}

View File

@@ -0,0 +1,55 @@
import { ServiceError } from '@/exceptions';
import { IAccount, IVendorCredit } from '@/interfaces';
import { Service, Inject } from 'typedi';
import BaseVendorCredit from '../BaseVendorCredit';
import { ERRORS } from './constants';
@Service()
export default class RefundVendorCredit extends BaseVendorCredit {
/**
* Retrieve the vendor credit refund or throw not found service error.
* @param {number} tenantId
* @param {number} vendorCreditId
* @returns
*/
public getRefundVendorCreditOrThrowError = async (
tenantId: number,
refundVendorCreditId: number
) => {
const { RefundVendorCredit } = this.tenancy.models(tenantId);
const refundCredit = await RefundVendorCredit.query().findById(
refundVendorCreditId
);
if (!refundCredit) {
throw new ServiceError(ERRORS.REFUND_VENDOR_CREDIT_NOT_FOUND);
}
return refundCredit;
};
/**
* Validate the deposit refund account type.
* @param {IAccount} account
*/
public validateRefundDepositAccountType = (account: IAccount): void => {
const supportedTypes = ['bank', 'cash', 'fixed-asset'];
if (supportedTypes.indexOf(account.accountType) === -1) {
throw new ServiceError(ERRORS.DEPOSIT_ACCOUNT_INVALID_TYPE);
}
};
/**
* Validate vendor credit has remaining credits.
* @param {IVendorCredit} vendorCredit
* @param {number} amount
*/
public validateVendorCreditRemainingCredit = (
vendorCredit: IVendorCredit,
amount: number
) => {
if (vendorCredit.creditsRemaining < amount) {
throw new ServiceError(ERRORS.VENDOR_CREDIT_HAS_NO_CREDITS_REMAINING);
}
};
}

View File

@@ -0,0 +1,159 @@
import { Inject, Service } from 'typedi';
import { Knex } from 'knex';
import { AccountNormal, ILedgerEntry } from '@/interfaces';
import { IRefundVendorCredit } from '@/interfaces';
import JournalPosterService from '@/services/Sales/JournalPosterService';
import LedgerRepository from '@/services/Ledger/LedgerRepository';
import HasTenancyService from '@/services/Tenancy/TenancyService';
@Service()
export default class RefundVendorCreditGLEntries {
@Inject()
private journalService: JournalPosterService;
@Inject()
private ledgerRepository: LedgerRepository;
@Inject()
private tenancy: HasTenancyService;
/**
* Retrieves the refund credit common GL entry.
* @param {IRefundVendorCredit} refundCredit
*/
private getRefundCreditGLCommonEntry = (
refundCredit: IRefundVendorCredit
) => {
return {
exchangeRate: refundCredit.exchangeRate,
currencyCode: refundCredit.currencyCode,
transactionType: 'RefundVendorCredit',
transactionId: refundCredit.id,
date: refundCredit.date,
userId: refundCredit.userId,
referenceNumber: refundCredit.referenceNo,
createdAt: refundCredit.createdAt,
indexGroup: 10,
credit: 0,
debit: 0,
note: refundCredit.description,
branchId: refundCredit.branchId,
};
};
/**
* Retrieves the refund credit payable GL entry.
* @param {IRefundVendorCredit} refundCredit
* @param {number} APAccountId
* @returns {ILedgerEntry}
*/
private getRefundCreditGLPayableEntry = (
refundCredit: IRefundVendorCredit,
APAccountId: number
): ILedgerEntry => {
const commonEntry = this.getRefundCreditGLCommonEntry(refundCredit);
return {
...commonEntry,
credit: refundCredit.amount,
accountId: APAccountId,
contactId: refundCredit.vendorCredit.vendorId,
index: 1,
accountNormal: AccountNormal.CREDIT,
};
};
/**
* Retrieves the refund credit deposit GL entry.
* @param {IRefundVendorCredit} refundCredit
* @returns {ILedgerEntry}
*/
private getRefundCreditGLDepositEntry = (
refundCredit: IRefundVendorCredit
): ILedgerEntry => {
const commonEntry = this.getRefundCreditGLCommonEntry(refundCredit);
return {
...commonEntry,
debit: refundCredit.amount,
accountId: refundCredit.depositAccountId,
index: 2,
accountNormal: AccountNormal.DEBIT,
};
};
/**
* Retrieve refund vendor credit GL entries.
* @param {IRefundVendorCredit} refundCredit
* @param {number} APAccountId
* @returns {ILedgerEntry[]}
*/
public getRefundCreditGLEntries = (
refundCredit: IRefundVendorCredit,
APAccountId: number
): ILedgerEntry[] => {
const payableEntry = this.getRefundCreditGLPayableEntry(
refundCredit,
APAccountId
);
const depositEntry = this.getRefundCreditGLDepositEntry(refundCredit);
return [payableEntry, depositEntry];
};
/**
* Saves refund credit note GL entries.
* @param {number} tenantId
* @param {IRefundVendorCredit} refundCredit -
* @param {Knex.Transaction} trx -
* @return {Promise<void>}
*/
public saveRefundCreditGLEntries = async (
tenantId: number,
refundCreditId: number,
trx?: Knex.Transaction
): Promise<void> => {
const { Account, RefundVendorCredit } = this.tenancy.models(tenantId);
// Retireve refund with associated vendor credit entity.
const refundCredit = await RefundVendorCredit.query()
.findById(refundCreditId)
.withGraphFetched('vendorCredit');
const payableAccount = await Account.query().findOne(
'slug',
'accounts-payable'
);
// Generates the GL entries of the given refund credit.
const entries = this.getRefundCreditGLEntries(
refundCredit,
payableAccount.id
);
// Saves the ledegr to the storage.
await this.ledgerRepository.saveLedgerEntries(tenantId, entries, trx);
};
/**
* Reverts refund credit note GL entries.
* @param {number} tenantId
* @param {number} refundCreditId
* @param {Knex.Transaction} trx
* @return {Promise<void>}
*/
public revertRefundCreditGLEntries = async (
tenantId: number,
refundCreditId: number,
trx?: Knex.Transaction
) => {
await this.journalService.revertJournalTransactions(
tenantId,
refundCreditId,
'RefundVendorCredit',
trx
);
};
}

View File

@@ -0,0 +1,59 @@
import { Service, Inject } from 'typedi';
import events from '@/subscribers/events';
import RefundVendorCreditGLEntries from './RefundVendorCreditGLEntries';
import {
IRefundCreditNoteDeletedPayload,
IRefundVendorCreditCreatedPayload,
} from '@/interfaces';
@Service()
export default class RefundVendorCreditGLEntriesSubscriber {
@Inject()
refundVendorGLEntries: RefundVendorCreditGLEntries;
/**
* Attaches events with handlers.
*/
attach(bus) {
bus.subscribe(
events.vendorCredit.onRefundCreated,
this.writeRefundVendorCreditGLEntriesOnceCreated
);
bus.subscribe(
events.vendorCredit.onRefundDeleted,
this.revertRefundVendorCreditOnceDeleted
);
}
/**
* Writes refund vendor credit GL entries once the transaction created.
* @param {IRefundCreditNoteCreatedPayload} payload -
*/
private writeRefundVendorCreditGLEntriesOnceCreated = async ({
tenantId,
trx,
refundVendorCredit,
}: IRefundVendorCreditCreatedPayload) => {
await this.refundVendorGLEntries.saveRefundCreditGLEntries(
tenantId,
refundVendorCredit.id,
trx
);
};
/**
* Reverts refund vendor credit GL entries once the transaction deleted.
* @param {IRefundCreditNoteDeletedPayload} payload -
*/
private revertRefundVendorCreditOnceDeleted = async ({
tenantId,
trx,
refundCreditId,
}: IRefundCreditNoteDeletedPayload) => {
await this.refundVendorGLEntries.revertRefundCreditGLEntries(
tenantId,
refundCreditId,
trx
);
};
}

View File

@@ -0,0 +1,30 @@
import { Transformer } from '@/lib/Transformer/Transformer';
import { formatNumber } from 'utils';
export class RefundVendorCreditTransformer extends Transformer {
/**
* Includeded attributes.
* @returns {string[]}
*/
public includeAttributes = (): string[] => {
return ['formttedAmount', 'formattedDate'];
};
/**
* Formatted amount.
* @returns {string}
*/
protected formttedAmount = (item) => {
return formatNumber(item.amount, {
currencyCode: item.currencyCode,
});
};
/**
* Formatted date.
* @returns {string}
*/
protected formattedDate = (item) => {
return this.formatDate(item.date);
};
}

View File

@@ -0,0 +1,5 @@
export const ERRORS = {
REFUND_VENDOR_CREDIT_NOT_FOUND: 'REFUND_VENDOR_CREDIT_NOT_FOUND',
DEPOSIT_ACCOUNT_INVALID_TYPE: 'DEPOSIT_ACCOUNT_INVALID_TYPE',
VENDOR_CREDIT_HAS_NO_CREDITS_REMAINING: 'VENDOR_CREDIT_HAS_NO_CREDITS_REMAINING'
}