mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 13:50:31 +00:00
add server to monorepo.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import Knex from 'knex';
|
||||
import { IVendorCreditAppliedBill } from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import Bluebird from 'bluebird';
|
||||
|
||||
@Service()
|
||||
export default class ApplyVendorCreditSyncBills {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Increment bills credited amount.
|
||||
* @param {number} tenantId
|
||||
* @param {IVendorCreditAppliedBill[]} vendorCreditAppliedBills
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public incrementBillsCreditedAmount = async (
|
||||
tenantId: number,
|
||||
vendorCreditAppliedBills: IVendorCreditAppliedBill[],
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
const { Bill } = this.tenancy.models(tenantId);
|
||||
|
||||
await Bluebird.each(
|
||||
vendorCreditAppliedBills,
|
||||
(vendorCreditAppliedBill: IVendorCreditAppliedBill) => {
|
||||
return Bill.query(trx)
|
||||
.where('id', vendorCreditAppliedBill.billId)
|
||||
.increment('creditedAmount', vendorCreditAppliedBill.amount);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Decrement bill credited amount.
|
||||
* @param {number} tenantId
|
||||
* @param {IVendorCreditAppliedBill} vendorCreditAppliedBill
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public decrementBillCreditedAmount = async (
|
||||
tenantId: number,
|
||||
vendorCreditAppliedBill: IVendorCreditAppliedBill,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
const { Bill } = this.tenancy.models(tenantId);
|
||||
|
||||
await Bill.query(trx)
|
||||
.findById(vendorCreditAppliedBill.billId)
|
||||
.decrement('creditedAmount', vendorCreditAppliedBill.amount);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import events from '@/subscribers/events';
|
||||
import {
|
||||
IVendorCreditApplyToBillDeletedPayload,
|
||||
IVendorCreditApplyToBillsCreatedPayload,
|
||||
} from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import ApplyVendorCreditSyncBills from './ApplyVendorCreditSyncBills';
|
||||
|
||||
@Service()
|
||||
export default class ApplyVendorCreditSyncBillsSubscriber {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
syncBillsWithVendorCredit: ApplyVendorCreditSyncBills;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
attach(bus) {
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onApplyToInvoicesCreated,
|
||||
this.incrementAppliedBillsOnceCreditCreated
|
||||
);
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onApplyToInvoicesDeleted,
|
||||
this.decrementAppliedBillsOnceCreditDeleted
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment credited amount of applied bills once the vendor credit
|
||||
* transaction created.
|
||||
* @param {IVendorCreditApplyToBillsCreatedPayload} paylaod -
|
||||
*/
|
||||
private incrementAppliedBillsOnceCreditCreated = async ({
|
||||
tenantId,
|
||||
vendorCreditAppliedBills,
|
||||
trx,
|
||||
}: IVendorCreditApplyToBillsCreatedPayload) => {
|
||||
await this.syncBillsWithVendorCredit.incrementBillsCreditedAmount(
|
||||
tenantId,
|
||||
vendorCreditAppliedBills,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Decrement credited amount of applied bills once the vendor credit
|
||||
* transaction delted.
|
||||
* @param {IVendorCreditApplyToBillDeletedPayload} payload
|
||||
*/
|
||||
private decrementAppliedBillsOnceCreditDeleted = async ({
|
||||
oldCreditAppliedToBill,
|
||||
tenantId,
|
||||
trx,
|
||||
}: IVendorCreditApplyToBillDeletedPayload) => {
|
||||
await this.syncBillsWithVendorCredit.decrementBillCreditedAmount(
|
||||
tenantId,
|
||||
oldCreditAppliedToBill,
|
||||
trx
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export default class ApplyVendorCreditSyncInvoiced {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Increment vendor credit invoiced amount.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorCreditId
|
||||
* @param {number} amount
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public incrementVendorCreditInvoicedAmount = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number,
|
||||
amount: number,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
const { VendorCredit } = this.tenancy.models(tenantId);
|
||||
|
||||
await VendorCredit.query(trx)
|
||||
.findById(vendorCreditId)
|
||||
.increment('invoicedAmount', amount);
|
||||
};
|
||||
|
||||
/**
|
||||
* Decrement credit note invoiced amount.
|
||||
* @param {number} tenantId
|
||||
* @param {number} creditNoteId
|
||||
* @param {number} invoicesAppliedAmount
|
||||
*/
|
||||
public decrementVendorCreditInvoicedAmount = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number,
|
||||
amount: number,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
const { VendorCredit } = this.tenancy.models(tenantId);
|
||||
|
||||
await VendorCredit.query(trx)
|
||||
.findById(vendorCreditId)
|
||||
.decrement('invoicedAmount', amount);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { sumBy } from 'lodash';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import ApplyVendorCreditSyncInvoiced from './ApplyVendorCreditSyncInvoiced';
|
||||
import events from '@/subscribers/events';
|
||||
import {
|
||||
IVendorCreditApplyToBillDeletedPayload,
|
||||
IVendorCreditApplyToBillsCreatedPayload,
|
||||
} from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export default class ApplyVendorCreditSyncInvoicedSubscriber {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
syncCreditWithInvoiced: ApplyVendorCreditSyncInvoiced;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
attach(bus) {
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onApplyToInvoicesCreated,
|
||||
this.incrementBillInvoicedOnceCreditApplied
|
||||
);
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onApplyToInvoicesDeleted,
|
||||
this.decrementBillInvoicedOnceCreditApplyDeleted
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment vendor credit invoiced amount once the apply transaction created.
|
||||
* @param {IVendorCreditApplyToBillsCreatedPayload} payload -
|
||||
*/
|
||||
private incrementBillInvoicedOnceCreditApplied = async ({
|
||||
vendorCredit,
|
||||
tenantId,
|
||||
vendorCreditAppliedBills,
|
||||
trx,
|
||||
}: IVendorCreditApplyToBillsCreatedPayload) => {
|
||||
const amount = sumBy(vendorCreditAppliedBills, 'amount');
|
||||
|
||||
await this.syncCreditWithInvoiced.incrementVendorCreditInvoicedAmount(
|
||||
tenantId,
|
||||
vendorCredit.id,
|
||||
amount,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Decrement vendor credit invoiced amount once the apply transaction deleted.
|
||||
* @param {IVendorCreditApplyToBillDeletedPayload} payload -
|
||||
*/
|
||||
private decrementBillInvoicedOnceCreditApplyDeleted = async ({
|
||||
tenantId,
|
||||
vendorCredit,
|
||||
oldCreditAppliedToBill,
|
||||
trx,
|
||||
}: IVendorCreditApplyToBillDeletedPayload) => {
|
||||
await this.syncCreditWithInvoiced.decrementVendorCreditInvoicedAmount(
|
||||
tenantId,
|
||||
oldCreditAppliedToBill.vendorCreditId,
|
||||
oldCreditAppliedToBill.amount,
|
||||
trx
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import Knex from 'knex';
|
||||
import { sumBy } from 'lodash';
|
||||
import {
|
||||
IVendorCredit,
|
||||
IVendorCreditApplyToBillsCreatedPayload,
|
||||
IVendorCreditApplyToInvoicesDTO,
|
||||
IVendorCreditApplyToInvoicesModel,
|
||||
IBill,
|
||||
} from '@/interfaces';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import PaymentReceiveService from '@/services/Sales/PaymentReceives/PaymentsReceives';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
import VendorCredit from '../BaseVendorCredit';
|
||||
import BillPaymentsService from '@/services/Purchases/BillPayments/BillPayments';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { ERRORS } from '../constants';
|
||||
|
||||
@Service()
|
||||
export default class ApplyVendorCreditToBills extends VendorCredit {
|
||||
@Inject('PaymentReceives')
|
||||
paymentReceive: PaymentReceiveService;
|
||||
|
||||
@Inject()
|
||||
uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
billPayment: BillPaymentsService;
|
||||
|
||||
/**
|
||||
* Apply credit note to the given invoices.
|
||||
* @param {number} tenantId
|
||||
* @param {number} creditNoteId
|
||||
* @param {IApplyCreditToInvoicesDTO} applyCreditToInvoicesDTO
|
||||
*/
|
||||
public applyVendorCreditToBills = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number,
|
||||
applyCreditToBillsDTO: IVendorCreditApplyToInvoicesDTO
|
||||
): Promise<void> => {
|
||||
const { VendorCreditAppliedBill } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves the vendor credit or throw not found service error.
|
||||
const vendorCredit = await this.getVendorCreditOrThrowError(
|
||||
tenantId,
|
||||
vendorCreditId
|
||||
);
|
||||
// Transfomes credit apply to bills DTO to model object.
|
||||
const vendorCreditAppliedModel = this.transformApplyDTOToModel(
|
||||
applyCreditToBillsDTO,
|
||||
vendorCredit
|
||||
);
|
||||
// Validate bills entries existance.
|
||||
const appliedBills = await this.billPayment.validateBillsExistance(
|
||||
tenantId,
|
||||
vendorCreditAppliedModel.entries,
|
||||
vendorCredit.vendorId
|
||||
);
|
||||
// Validate bills has remaining amount to apply.
|
||||
this.validateBillsRemainingAmount(
|
||||
appliedBills,
|
||||
vendorCreditAppliedModel.amount
|
||||
);
|
||||
// Validate vendor credit remaining credit amount.
|
||||
this.validateCreditRemainingAmount(
|
||||
vendorCredit,
|
||||
vendorCreditAppliedModel.amount
|
||||
);
|
||||
// Saves vendor credit applied to bills under unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Inserts vendor credit applied to bills graph to the storage layer.
|
||||
const vendorCreditAppliedBills =
|
||||
await VendorCreditAppliedBill.query().insertGraph(
|
||||
vendorCreditAppliedModel.entries
|
||||
);
|
||||
// Triggers `IVendorCreditApplyToBillsCreatedPayload` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.vendorCredit.onApplyToInvoicesCreated,
|
||||
{
|
||||
trx,
|
||||
tenantId,
|
||||
vendorCredit,
|
||||
vendorCreditAppliedBills,
|
||||
} as IVendorCreditApplyToBillsCreatedPayload
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Transformes apply DTO to model.
|
||||
* @param {IApplyCreditToInvoicesDTO} applyDTO
|
||||
* @param {ICreditNote} creditNote
|
||||
* @returns {IVendorCreditApplyToInvoicesModel}
|
||||
*/
|
||||
private transformApplyDTOToModel = (
|
||||
applyDTO: IVendorCreditApplyToInvoicesDTO,
|
||||
vendorCredit: IVendorCredit
|
||||
): IVendorCreditApplyToInvoicesModel => {
|
||||
const entries = applyDTO.entries.map((entry) => ({
|
||||
billId: entry.billId,
|
||||
amount: entry.amount,
|
||||
vendorCreditId: vendorCredit.id,
|
||||
}));
|
||||
const amount = sumBy(applyDTO.entries, 'amount');
|
||||
|
||||
return {
|
||||
amount,
|
||||
entries,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate bills remaining amount.
|
||||
* @param {IBill[]} bills
|
||||
* @param {number} amount
|
||||
*/
|
||||
private validateBillsRemainingAmount = (bills: IBill[], amount: number) => {
|
||||
const invalidBills = bills.filter((bill) => bill.dueAmount < amount);
|
||||
if (invalidBills.length > 0) {
|
||||
throw new ServiceError(ERRORS.BILLS_HAS_NO_REMAINING_AMOUNT);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { IVendorCreditApplyToBillDeletedPayload } from '@/interfaces';
|
||||
import Knex from 'knex';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import BaseVendorCredit from '../BaseVendorCredit';
|
||||
import { ERRORS } from '../constants';
|
||||
|
||||
@Service()
|
||||
export default class DeleteApplyVendorCreditToBill extends BaseVendorCredit {
|
||||
@Inject()
|
||||
uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
eventPublisher: EventPublisher;
|
||||
|
||||
/**
|
||||
* Delete apply vendor credit to bill transaction.
|
||||
* @param {number} tenantId
|
||||
* @param {number} appliedCreditToBillId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public deleteApplyVendorCreditToBills = async (
|
||||
tenantId: number,
|
||||
appliedCreditToBillId: number
|
||||
) => {
|
||||
const { VendorCreditAppliedBill } = this.tenancy.models(tenantId);
|
||||
|
||||
const oldCreditAppliedToBill =
|
||||
await VendorCreditAppliedBill.query().findById(appliedCreditToBillId);
|
||||
|
||||
if (!oldCreditAppliedToBill) {
|
||||
throw new ServiceError(ERRORS.VENDOR_CREDIT_APPLY_TO_BILLS_NOT_FOUND);
|
||||
}
|
||||
// Retrieve the vendor credit or throw not found service error.
|
||||
const vendorCredit = await this.getVendorCreditOrThrowError(
|
||||
tenantId,
|
||||
oldCreditAppliedToBill.vendorCreditId
|
||||
);
|
||||
// Deletes vendor credit apply under unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Delete vendor credit applied to bill transaction.
|
||||
await VendorCreditAppliedBill.query(trx)
|
||||
.findById(appliedCreditToBillId)
|
||||
.delete();
|
||||
|
||||
// Triggers `onVendorCreditApplyToInvoiceDeleted` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.vendorCredit.onApplyToInvoicesDeleted,
|
||||
{
|
||||
tenantId,
|
||||
vendorCredit,
|
||||
oldCreditAppliedToBill,
|
||||
trx,
|
||||
} as IVendorCreditApplyToBillDeletedPayload
|
||||
);
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import BaseVendorCredit from '../BaseVendorCredit';
|
||||
import { VendorCreditAppliedBillTransformer } from './VendorCreditAppliedBillTransformer';
|
||||
|
||||
@Service()
|
||||
export default class GetAppliedBillsToVendorCredit extends BaseVendorCredit {
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorCreditId
|
||||
* @returns
|
||||
*/
|
||||
public getAppliedBills = async (tenantId: number, vendorCreditId: number) => {
|
||||
const { VendorCreditAppliedBill } = this.tenancy.models(tenantId);
|
||||
|
||||
const vendorCredit = await this.getVendorCreditOrThrowError(
|
||||
tenantId,
|
||||
vendorCreditId
|
||||
);
|
||||
const appliedToBills = await VendorCreditAppliedBill.query()
|
||||
.where('vendorCreditId', vendorCreditId)
|
||||
.withGraphFetched('bill')
|
||||
.withGraphFetched('vendorCredit');
|
||||
|
||||
// Transformes the models to POJO.
|
||||
return this.transformer.transform(
|
||||
tenantId,
|
||||
appliedToBills,
|
||||
new VendorCreditAppliedBillTransformer()
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import BaseVendorCredit from '../BaseVendorCredit';
|
||||
import { VendorCreditToApplyBillTransformer } from './VendorCreditToApplyBillTransformer';
|
||||
|
||||
@Service()
|
||||
export default class GetVendorCreditToApplyBills extends BaseVendorCredit {
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Retrieve bills that valid apply to the given vendor credit.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorCreditId
|
||||
* @returns
|
||||
*/
|
||||
public getCreditToApplyBills = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number
|
||||
) => {
|
||||
const { Bill } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve vendor credit or throw not found service error.
|
||||
const vendorCredit = await this.getVendorCreditOrThrowError(
|
||||
tenantId,
|
||||
vendorCreditId
|
||||
);
|
||||
// Retrieive open bills associated to the given vendor.
|
||||
const openBills = await Bill.query()
|
||||
.where('vendor_id', vendorCredit.vendorId)
|
||||
.modify('dueBills')
|
||||
.modify('published');
|
||||
|
||||
// Transformes the bills to POJO.
|
||||
return this.transformer.transform(
|
||||
tenantId,
|
||||
openBills,
|
||||
new VendorCreditToApplyBillTransformer()
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
import { formatNumber } from 'utils';
|
||||
|
||||
export class VendorCreditAppliedBillTransformer extends Transformer {
|
||||
/**
|
||||
* Includeded attributes.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'formattedAmount',
|
||||
'vendorCreditNumber',
|
||||
'vendorCreditDate',
|
||||
'billNumber',
|
||||
'billReferenceNo',
|
||||
'formattedVendorCreditDate',
|
||||
'formattedBillDate',
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclude attributes.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['bill', 'vendorCredit'];
|
||||
};
|
||||
|
||||
protected formattedAmount = (item) => {
|
||||
return formatNumber(item.amount, {
|
||||
currencyCode: item.vendorCredit.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
protected vendorCreditNumber = (item) => {
|
||||
return item.vendorCredit.vendorCreditNumber;
|
||||
};
|
||||
|
||||
protected vendorCreditDate = (item) => {
|
||||
return item.vendorCredit.vendorCreditDate;
|
||||
};
|
||||
|
||||
protected formattedVendorCreditDate = (item) => {
|
||||
return this.formatDate(item.vendorCredit.vendorCreditDate);
|
||||
};
|
||||
|
||||
protected billNumber = (item) => {
|
||||
return item.bill.billNo;
|
||||
};
|
||||
|
||||
protected billReferenceNo = (item) => {
|
||||
return item.bill.referenceNo;
|
||||
};
|
||||
|
||||
protected BillDate = (item) => {
|
||||
return item.bill.billDate;
|
||||
};
|
||||
|
||||
protected formattedBillDate = (item) => {
|
||||
return this.formatDate(item.bill.billDate);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { IBill } from '@/interfaces';
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
import { formatNumber } from 'utils';
|
||||
|
||||
export class VendorCreditToApplyBillTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to sale invoice object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'formattedBillDate',
|
||||
'formattedDueDate',
|
||||
'formattedAmount',
|
||||
'formattedDueAmount',
|
||||
'formattedPaymentAmount',
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted bill date.
|
||||
* @param {IBill} bill
|
||||
* @returns {String}
|
||||
*/
|
||||
protected formattedBillDate = (bill: IBill): string => {
|
||||
return this.formatDate(bill.billDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted due date.
|
||||
* @param {IBill} bill
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedDueDate = (bill: IBill): string => {
|
||||
return this.formatDate(bill.dueDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted bill amount.
|
||||
* @param {IBill} bill
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedAmount = (bill: IBill): string => {
|
||||
return formatNumber(bill.amount, {
|
||||
currencyCode: bill.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted bill due amount.
|
||||
* @param {IBill} bill
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedDueAmount = (bill: IBill): string => {
|
||||
return formatNumber(bill.dueAmount, {
|
||||
currencyCode: bill.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted payment amount.
|
||||
* @param {IBill} bill
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedPaymentAmount = (bill: IBill): string => {
|
||||
return formatNumber(bill.paymentAmount, {
|
||||
currencyCode: bill.currencyCode,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import moment from 'moment';
|
||||
import { omit } from 'lodash';
|
||||
import * as R from 'ramda';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { ERRORS } from './constants';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import {
|
||||
IVendorCredit,
|
||||
IVendorCreditCreateDTO,
|
||||
IVendorCreditEditDTO,
|
||||
} from '@/interfaces';
|
||||
import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
|
||||
import AutoIncrementOrdersService from '@/services/Sales/AutoIncrementOrdersService';
|
||||
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
|
||||
import { WarehouseTransactionDTOTransform } from '@/services/Warehouses/Integrations/WarehouseTransactionDTOTransform';
|
||||
|
||||
@Service()
|
||||
export default class BaseVendorCredit {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private itemsEntriesService: ItemsEntriesService;
|
||||
|
||||
@Inject()
|
||||
private autoIncrementOrdersService: AutoIncrementOrdersService;
|
||||
|
||||
@Inject()
|
||||
private branchDTOTransform: BranchTransactionDTOTransform;
|
||||
|
||||
@Inject()
|
||||
private warehouseDTOTransform: WarehouseTransactionDTOTransform;
|
||||
|
||||
/**
|
||||
* Transformes the credit/edit vendor credit DTO to model.
|
||||
* @param {number} tenantId -
|
||||
* @param {IVendorCreditCreateDTO | IVendorCreditEditDTO} vendorCreditDTO
|
||||
* @param {string} vendorCurrencyCode -
|
||||
* @param {IVendorCredit} oldVendorCredit -
|
||||
* @returns {IVendorCredit}
|
||||
*/
|
||||
public transformCreateEditDTOToModel = (
|
||||
tenantId: number,
|
||||
vendorCreditDTO: IVendorCreditCreateDTO | IVendorCreditEditDTO,
|
||||
vendorCurrencyCode: string,
|
||||
oldVendorCredit?: IVendorCredit
|
||||
): IVendorCredit => {
|
||||
// Calculates the total amount of items entries.
|
||||
const amount = this.itemsEntriesService.getTotalItemsEntries(
|
||||
vendorCreditDTO.entries
|
||||
);
|
||||
const entries = vendorCreditDTO.entries.map((entry) => ({
|
||||
...entry,
|
||||
referenceType: 'VendorCredit',
|
||||
}));
|
||||
// Retreive the next vendor credit number.
|
||||
const autoNextNumber = this.getNextCreditNumber(tenantId);
|
||||
|
||||
// Detarmines the credit note number.
|
||||
const vendorCreditNumber =
|
||||
vendorCreditDTO.vendorCreditNumber ||
|
||||
oldVendorCredit?.vendorCreditNumber ||
|
||||
autoNextNumber;
|
||||
|
||||
const initialDTO = {
|
||||
...omit(vendorCreditDTO, ['open']),
|
||||
amount,
|
||||
currencyCode: vendorCurrencyCode,
|
||||
exchangeRate: vendorCreditDTO.exchangeRate || 1,
|
||||
vendorCreditNumber,
|
||||
entries,
|
||||
...(vendorCreditDTO.open &&
|
||||
!oldVendorCredit?.openedAt && {
|
||||
openedAt: moment().toMySqlDateTime(),
|
||||
}),
|
||||
};
|
||||
return R.compose(
|
||||
this.branchDTOTransform.transformDTO<IVendorCredit>(tenantId),
|
||||
this.warehouseDTOTransform.transformDTO<IVendorCredit>(tenantId)
|
||||
)(initialDTO);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the vendor credit or throw not found service error.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorCreditId
|
||||
*/
|
||||
public getVendorCreditOrThrowError = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number
|
||||
): Promise<IVendorCredit> => {
|
||||
const { VendorCredit } = this.tenancy.models(tenantId);
|
||||
|
||||
const vendorCredit = await VendorCredit.query().findById(vendorCreditId);
|
||||
|
||||
if (!vendorCredit) {
|
||||
throw new ServiceError(ERRORS.VENDOR_CREDIT_NOT_FOUND);
|
||||
}
|
||||
return vendorCredit;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the next unique credit number.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @return {string}
|
||||
*/
|
||||
private getNextCreditNumber = (tenantId: number): string => {
|
||||
return this.autoIncrementOrdersService.getNextTransactionNumber(
|
||||
tenantId,
|
||||
'vendor_credit'
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Increment the vendor credit serial next number.
|
||||
* @param {number} tenantId -
|
||||
*/
|
||||
public incrementSerialNumber = (tenantId: number) => {
|
||||
return this.autoIncrementOrdersService.incrementSettingsNextNumber(
|
||||
tenantId,
|
||||
'vendor_credit'
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate the credit note remaining amount.
|
||||
* @param {ICreditNote} creditNote
|
||||
* @param {number} amount
|
||||
*/
|
||||
public validateCreditRemainingAmount = (
|
||||
vendorCredit: IVendorCredit,
|
||||
amount: number
|
||||
) => {
|
||||
if (vendorCredit.creditsRemaining < amount) {
|
||||
throw new ServiceError(ERRORS.VENDOR_CREDIT_HAS_NO_REMAINING_AMOUNT);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
IVendorCreditCreatedPayload,
|
||||
IVendorCreditCreateDTO,
|
||||
IVendorCreditCreatingPayload,
|
||||
} from '@/interfaces';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
import BaseVendorCredit from './BaseVendorCredit';
|
||||
|
||||
@Service()
|
||||
export default class CreateVendorCredit extends BaseVendorCredit {
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private itemsEntriesService: ItemsEntriesService;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
/**
|
||||
* Creates a new vendor credit.
|
||||
* @param {number} tenantId -
|
||||
* @param {IVendorCreditCreateDTO} vendorCreditCreateDTO -
|
||||
*/
|
||||
public newVendorCredit = async (
|
||||
tenantId: number,
|
||||
vendorCreditCreateDTO: IVendorCreditCreateDTO
|
||||
) => {
|
||||
const { VendorCredit, Vendor } = this.tenancy.models(tenantId);
|
||||
|
||||
// Triggers `onVendorCreditCreate` event.
|
||||
await this.eventPublisher.emitAsync(events.vendorCredit.onCreate, {
|
||||
tenantId,
|
||||
vendorCreditCreateDTO,
|
||||
});
|
||||
// Retrieve the given vendor or throw not found service error.
|
||||
const vendor = await Vendor.query()
|
||||
.findById(vendorCreditCreateDTO.vendorId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Validate items should be sellable items.
|
||||
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
||||
tenantId,
|
||||
vendorCreditCreateDTO.entries
|
||||
);
|
||||
// Transformes the credit DTO to storage layer.
|
||||
const vendorCreditModel = this.transformCreateEditDTOToModel(
|
||||
tenantId,
|
||||
vendorCreditCreateDTO,
|
||||
vendor.currencyCode
|
||||
);
|
||||
// Saves the vendor credit transactions under UOW envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onVendorCreditCreating` event.
|
||||
await this.eventPublisher.emitAsync(events.vendorCredit.onCreating, {
|
||||
tenantId,
|
||||
vendorCreditCreateDTO,
|
||||
trx,
|
||||
} as IVendorCreditCreatingPayload);
|
||||
|
||||
// Saves the vendor credit graph.
|
||||
const vendorCredit = await VendorCredit.query(trx).upsertGraphAndFetch({
|
||||
...vendorCreditModel,
|
||||
});
|
||||
// Triggers `onVendorCreditCreated` event.
|
||||
await this.eventPublisher.emitAsync(events.vendorCredit.onCreated, {
|
||||
tenantId,
|
||||
vendorCredit,
|
||||
vendorCreditCreateDTO,
|
||||
trx,
|
||||
} as IVendorCreditCreatedPayload);
|
||||
|
||||
return vendorCredit;
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import events from '@/subscribers/events';
|
||||
import { IVendorEventDeletingPayload } from '@/interfaces';
|
||||
|
||||
const ERRORS = {
|
||||
VENDOR_HAS_TRANSACTIONS: 'VENDOR_HAS_TRANSACTIONS',
|
||||
};
|
||||
|
||||
@Service()
|
||||
export default class DeleteVendorAssociatedVendorCredit {
|
||||
@Inject()
|
||||
tenancy: TenancyService;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
* @param bus
|
||||
*/
|
||||
public attach = (bus) => {
|
||||
bus.subscribe(
|
||||
events.vendors.onDeleting,
|
||||
this.validateVendorHasNoCreditsTransactionsOnceDeleting
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate vendor has no assocaited credit transaction once the vendor deleting.
|
||||
* @param {IVendorEventDeletingPayload} payload -
|
||||
*/
|
||||
public validateVendorHasNoCreditsTransactionsOnceDeleting = async ({
|
||||
tenantId,
|
||||
vendorId,
|
||||
}: IVendorEventDeletingPayload) => {
|
||||
await this.validateVendorHasNoCreditsTransactions(tenantId, vendorId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate the given vendor has no associated vendor credit transactions.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorId
|
||||
*/
|
||||
public validateVendorHasNoCreditsTransactions = async (
|
||||
tenantId: number,
|
||||
vendorId: number
|
||||
): Promise<void> => {
|
||||
const { VendorCredit } = this.tenancy.models(tenantId);
|
||||
|
||||
const associatedVendors = await VendorCredit.query().where(
|
||||
'vendorId',
|
||||
vendorId
|
||||
);
|
||||
if (associatedVendors.length > 0) {
|
||||
throw new ServiceError(ERRORS.VENDOR_HAS_TRANSACTIONS);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import BaseVendorCredit from './BaseVendorCredit';
|
||||
import { Knex } from 'knex';
|
||||
import events from '@/subscribers/events';
|
||||
import {
|
||||
IVendorCreditDeletedPayload,
|
||||
IVendorCreditDeletingPayload,
|
||||
} from '@/interfaces';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { ERRORS } from './constants';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export default class DeleteVendorCredit extends BaseVendorCredit {
|
||||
@Inject()
|
||||
uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Deletes the given vendor credit.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} vendorCreditId - Vendor credit id.
|
||||
*/
|
||||
public deleteVendorCredit = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number
|
||||
) => {
|
||||
const { VendorCredit, ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the old vendor credit.
|
||||
const oldVendorCredit = await this.getVendorCreditOrThrowError(
|
||||
tenantId,
|
||||
vendorCreditId
|
||||
);
|
||||
// Validates vendor credit has no associate refund transactions.
|
||||
await this.validateVendorCreditHasNoRefundTransactions(
|
||||
tenantId,
|
||||
vendorCreditId
|
||||
);
|
||||
// Validates vendor credit has no associated applied to bills transactions.
|
||||
await this.validateVendorCreditHasNoApplyBillsTransactions(
|
||||
tenantId,
|
||||
vendorCreditId
|
||||
);
|
||||
// Deletes the vendor credit transactions under UOW envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onVendorCreditEditing` event.
|
||||
await this.eventPublisher.emitAsync(events.vendorCredit.onDeleting, {
|
||||
tenantId,
|
||||
oldVendorCredit,
|
||||
trx,
|
||||
} as IVendorCreditDeletingPayload);
|
||||
|
||||
// Deletes the associated credit note entries.
|
||||
await ItemEntry.query(trx)
|
||||
.where('reference_id', vendorCreditId)
|
||||
.where('reference_type', 'VendorCredit')
|
||||
.delete();
|
||||
|
||||
// Deletes the credit note transaction.
|
||||
await VendorCredit.query(trx).findById(vendorCreditId).delete();
|
||||
|
||||
// Triggers `onVendorCreditDeleted` event.
|
||||
await this.eventPublisher.emitAsync(events.vendorCredit.onDeleted, {
|
||||
tenantId,
|
||||
vendorCreditId,
|
||||
oldVendorCredit,
|
||||
trx,
|
||||
} as IVendorCreditDeletedPayload);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates vendor credit has no refund transactions.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorCreditId
|
||||
*/
|
||||
private validateVendorCreditHasNoRefundTransactions = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number
|
||||
): Promise<void> => {
|
||||
const { RefundVendorCredit } = this.tenancy.models(tenantId);
|
||||
|
||||
const refundCredits = await RefundVendorCredit.query().where(
|
||||
'vendorCreditId',
|
||||
vendorCreditId
|
||||
);
|
||||
if (refundCredits.length > 0) {
|
||||
throw new ServiceError(ERRORS.VENDOR_CREDIT_HAS_REFUND_TRANSACTIONS);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate vendor credit has no applied transactions to bills.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorCreditId
|
||||
*/
|
||||
private validateVendorCreditHasNoApplyBillsTransactions = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number
|
||||
): Promise<void> => {
|
||||
const { VendorCreditAppliedBill } = this.tenancy.models(tenantId);
|
||||
|
||||
const appliedTransactions = await VendorCreditAppliedBill.query().where(
|
||||
'vendorCreditId',
|
||||
vendorCreditId
|
||||
);
|
||||
if (appliedTransactions.length > 0) {
|
||||
throw new ServiceError(ERRORS.VENDOR_CREDIT_HAS_APPLIED_BILLS);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import {
|
||||
IVendorCreditEditDTO,
|
||||
IVendorCreditEditedPayload,
|
||||
IVendorCreditEditingPayload,
|
||||
} from '@/interfaces';
|
||||
import BaseVendorCredit from './BaseVendorCredit';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
|
||||
import events from '@/subscribers/events';
|
||||
|
||||
@Service()
|
||||
export default class EditVendorCredit extends BaseVendorCredit {
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private itemsEntriesService: ItemsEntriesService;
|
||||
|
||||
/**
|
||||
* Deletes the given vendor credit.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} vendorCreditId - Vendor credit id.
|
||||
*/
|
||||
public editVendorCredit = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number,
|
||||
vendorCreditDTO: IVendorCreditEditDTO
|
||||
) => {
|
||||
const { VendorCredit } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the vendor credit or throw not found service error.
|
||||
const oldVendorCredit = await this.getVendorCreditOrThrowError(
|
||||
tenantId,
|
||||
vendorCreditId
|
||||
);
|
||||
// Validate customer existance.
|
||||
const vendor = await Contact.query()
|
||||
.modify('vendor')
|
||||
.findById(vendorCreditDTO.vendorId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Validate items ids existance.
|
||||
await this.itemsEntriesService.validateItemsIdsExistance(
|
||||
tenantId,
|
||||
vendorCreditDTO.entries
|
||||
);
|
||||
// Validate non-sellable entries items.
|
||||
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
||||
tenantId,
|
||||
vendorCreditDTO.entries
|
||||
);
|
||||
// Validate the items entries existance.
|
||||
await this.itemsEntriesService.validateEntriesIdsExistance(
|
||||
tenantId,
|
||||
vendorCreditId,
|
||||
'VendorCredit',
|
||||
vendorCreditDTO.entries
|
||||
);
|
||||
// Transformes edit DTO to model storage layer.
|
||||
const vendorCreditModel = this.transformCreateEditDTOToModel(
|
||||
tenantId,
|
||||
vendorCreditDTO,
|
||||
vendor.currencyCode,
|
||||
oldVendorCredit
|
||||
);
|
||||
// Edits the vendor credit graph under unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx) => {
|
||||
// Triggers `onVendorCreditEditing` event.
|
||||
await this.eventPublisher.emitAsync(events.vendorCredit.onEditing, {
|
||||
tenantId,
|
||||
oldVendorCredit,
|
||||
vendorCreditDTO,
|
||||
trx,
|
||||
} as IVendorCreditEditingPayload);
|
||||
|
||||
// Saves the vendor credit graph to the storage.
|
||||
const vendorCredit = await VendorCredit.query(trx).upsertGraphAndFetch({
|
||||
id: vendorCreditId,
|
||||
...vendorCreditModel,
|
||||
});
|
||||
// Triggers `onVendorCreditEdited event.
|
||||
await this.eventPublisher.emitAsync(events.vendorCredit.onEdited, {
|
||||
tenantId,
|
||||
oldVendorCredit,
|
||||
vendorCredit,
|
||||
vendorCreditId,
|
||||
trx,
|
||||
} as IVendorCreditEditedPayload);
|
||||
|
||||
return vendorCredit;
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { VendorCreditTransformer } from './VendorCreditTransformer';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { ERRORS } from './constants';
|
||||
|
||||
@Service()
|
||||
export default class GetVendorCredit {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Retrieve the given vendor credit.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} vendorCreditId - Vendor credit id.
|
||||
*/
|
||||
public getVendorCredit = async (tenantId: number, vendorCreditId: number) => {
|
||||
const { VendorCredit } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the vendor credit model graph.
|
||||
const vendorCredit = await VendorCredit.query()
|
||||
.findById(vendorCreditId)
|
||||
.withGraphFetched('entries.item')
|
||||
.withGraphFetched('vendor')
|
||||
.withGraphFetched('branch');
|
||||
|
||||
if (!vendorCredit) {
|
||||
throw new ServiceError(ERRORS.VENDOR_CREDIT_NOT_FOUND);
|
||||
}
|
||||
return this.transformer.transform(
|
||||
tenantId,
|
||||
vendorCredit,
|
||||
new VendorCreditTransformer()
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import * as R from 'ramda';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import BaseVendorCredit from './BaseVendorCredit';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import { IVendorCreditsQueryDTO } from '@/interfaces';
|
||||
import { VendorCreditTransformer } from './VendorCreditTransformer';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
|
||||
@Service()
|
||||
export default class ListVendorCredits extends BaseVendorCredit {
|
||||
@Inject()
|
||||
private dynamicListService: DynamicListingService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Parses the sale invoice list filter DTO.
|
||||
* @param {IVendorCreditsQueryDTO} filterDTO
|
||||
* @returns
|
||||
*/
|
||||
private parseListFilterDTO = (filterDTO: IVendorCreditsQueryDTO) => {
|
||||
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the vendor credits list.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {IVendorCreditsQueryDTO} vendorCreditQuery -
|
||||
*/
|
||||
public getVendorCredits = async (
|
||||
tenantId: number,
|
||||
vendorCreditQuery: IVendorCreditsQueryDTO
|
||||
) => {
|
||||
const { VendorCredit } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses stringified filter roles.
|
||||
const filter = this.parseListFilterDTO(vendorCreditQuery);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
VendorCredit,
|
||||
filter
|
||||
);
|
||||
const { results, pagination } = await VendorCredit.query()
|
||||
.onBuild((builder) => {
|
||||
builder.withGraphFetched('entries');
|
||||
builder.withGraphFetched('vendor');
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
})
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
// Transformes the vendor credits models to POJO.
|
||||
const vendorCredits = await this.transformer.transform(
|
||||
tenantId,
|
||||
results,
|
||||
new VendorCreditTransformer()
|
||||
);
|
||||
return {
|
||||
vendorCredits,
|
||||
pagination,
|
||||
filterMeta: dynamicFilter.getResponseMeta(),
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import {
|
||||
IVendorCredit,
|
||||
IVendorCreditOpenedPayload,
|
||||
IVendorCreditOpeningPayload,
|
||||
IVendorCreditOpenPayload,
|
||||
} from '@/interfaces';
|
||||
import Knex from 'knex';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import BaseVendorCredit from './BaseVendorCredit';
|
||||
import { ERRORS } from './constants';
|
||||
|
||||
@Service()
|
||||
export default class OpenVendorCredit extends BaseVendorCredit {
|
||||
@Inject()
|
||||
eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
uow: UnitOfWork;
|
||||
|
||||
/**
|
||||
* Opens the given credit note.
|
||||
* @param {number} tenantId -
|
||||
* @param {ICreditNoteEditDTO} creditNoteEditDTO -
|
||||
* @returns {Promise<ICreditNote>}
|
||||
*/
|
||||
public openVendorCredit = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number
|
||||
): Promise<IVendorCredit> => {
|
||||
const { VendorCredit } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the vendor credit or throw not found service error.
|
||||
const oldVendorCredit = await this.getVendorCreditOrThrowError(
|
||||
tenantId,
|
||||
vendorCreditId
|
||||
);
|
||||
// Throw service error if the credit note is already open.
|
||||
this.throwErrorIfAlreadyOpen(oldVendorCredit);
|
||||
|
||||
// Triggers `onVendorCreditOpen` event.
|
||||
await this.eventPublisher.emitAsync(events.vendorCredit.onOpen, {
|
||||
tenantId,
|
||||
vendorCreditId,
|
||||
oldVendorCredit,
|
||||
} as IVendorCreditOpenPayload);
|
||||
|
||||
// Sales the credit note transactions with associated entries.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
const eventPayload = {
|
||||
tenantId,
|
||||
vendorCreditId,
|
||||
oldVendorCredit,
|
||||
trx,
|
||||
} as IVendorCreditOpeningPayload;
|
||||
|
||||
// Triggers `onCreditNoteOpening` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.creditNote.onOpening,
|
||||
eventPayload as IVendorCreditOpeningPayload
|
||||
);
|
||||
// Saves the vendor credit graph to the storage.
|
||||
const vendorCredit = await VendorCredit.query(trx)
|
||||
.findById(vendorCreditId)
|
||||
.update({
|
||||
openedAt: new Date(),
|
||||
});
|
||||
// Triggers `onVendorCreditOpened` event.
|
||||
await this.eventPublisher.emitAsync(events.vendorCredit.onOpened, {
|
||||
...eventPayload,
|
||||
vendorCredit,
|
||||
} as IVendorCreditOpenedPayload);
|
||||
|
||||
return vendorCredit;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Throw error if the vendor credit is already open.
|
||||
* @param {IVendorCredit} vendorCredit
|
||||
*/
|
||||
public throwErrorIfAlreadyOpen = (vendorCredit: IVendorCredit) => {
|
||||
if (vendorCredit.openedAt) {
|
||||
throw new ServiceError(ERRORS.VENDOR_CREDIT_ALREADY_OPENED);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
@@ -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'
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import events from '@/subscribers/events';
|
||||
import BaseVendorCredit from './BaseVendorCredit';
|
||||
import { IVendorCreditCreatedPayload } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export default class VendorCreditAutoSerialSubscriber {
|
||||
@Inject()
|
||||
vendorCreditService: BaseVendorCredit;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(events.vendorCredit.onCreated, this.autoIncrementOnceCreated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto serial increment once the vendor credit created.
|
||||
* @param {IVendorCreditCreatedPayload} payload
|
||||
*/
|
||||
private autoIncrementOnceCreated = ({
|
||||
tenantId,
|
||||
}: IVendorCreditCreatedPayload) => {
|
||||
this.vendorCreditService.incrementSerialNumber(tenantId);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IVendorCredit,
|
||||
ILedgerEntry,
|
||||
AccountNormal,
|
||||
IItemEntry,
|
||||
} from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||
import Ledger from '@/services/Accounting/Ledger';
|
||||
|
||||
@Service()
|
||||
export default class VendorCreditGLEntries {
|
||||
@Inject()
|
||||
private ledgerStorage: LedgerStorageService;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Retrieve the vendor credit GL common entry.
|
||||
* @param {IVendorCredit} vendorCredit
|
||||
* @returns {}
|
||||
*/
|
||||
public getVendorCreditGLCommonEntry = (vendorCredit: IVendorCredit) => {
|
||||
return {
|
||||
date: vendorCredit.vendorCreditDate,
|
||||
currencyCode: vendorCredit.currencyCode,
|
||||
exchangeRate: vendorCredit.exchangeRate,
|
||||
|
||||
transactionId: vendorCredit.id,
|
||||
transactionType: 'VendorCredit',
|
||||
transactionNumber: vendorCredit.vendorCreditNumber,
|
||||
referenceNumber: vendorCredit.referenceNo,
|
||||
|
||||
credit: 0,
|
||||
debit: 0,
|
||||
|
||||
branchId: vendorCredit.branchId,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the vendor credit payable GL entry.
|
||||
* @param {IVendorCredit} vendorCredit
|
||||
* @param {number} APAccountId
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
public getVendorCreditPayableGLEntry = (
|
||||
vendorCredit: IVendorCredit,
|
||||
APAccountId: number
|
||||
): ILedgerEntry => {
|
||||
const commonEntity = this.getVendorCreditGLCommonEntry(vendorCredit);
|
||||
|
||||
return {
|
||||
...commonEntity,
|
||||
debit: vendorCredit.localAmount,
|
||||
accountId: APAccountId,
|
||||
contactId: vendorCredit.vendorId,
|
||||
accountNormal: AccountNormal.CREDIT,
|
||||
index: 1,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the vendor credit item GL entry.
|
||||
* @param {IVendorCredit} vendorCredit
|
||||
* @param {IItemEntry} entry
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
public getVendorCreditGLItemEntry = R.curry(
|
||||
(
|
||||
vendorCredit: IVendorCredit,
|
||||
entry: IItemEntry,
|
||||
index: number
|
||||
): ILedgerEntry => {
|
||||
const commonEntity = this.getVendorCreditGLCommonEntry(vendorCredit);
|
||||
const localAmount = entry.amount * vendorCredit.exchangeRate;
|
||||
|
||||
return {
|
||||
...commonEntity,
|
||||
credit: localAmount,
|
||||
index: index + 2,
|
||||
itemId: entry.itemId,
|
||||
itemQuantity: entry.quantity,
|
||||
accountId:
|
||||
'inventory' === entry.item.type
|
||||
? entry.item.inventoryAccountId
|
||||
: entry.costAccountId || entry.item.costAccountId,
|
||||
accountNormal: AccountNormal.DEBIT,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieve the vendor credit GL entries.
|
||||
* @param {IVendorCredit} vendorCredit -
|
||||
* @param {number} receivableAccount -
|
||||
* @return {ILedgerEntry[]}
|
||||
*/
|
||||
public getVendorCreditGLEntries = (
|
||||
vendorCredit: IVendorCredit,
|
||||
payableAccountId: number
|
||||
): ILedgerEntry[] => {
|
||||
const payableEntry = this.getVendorCreditPayableGLEntry(
|
||||
vendorCredit,
|
||||
payableAccountId
|
||||
);
|
||||
const getItemEntry = this.getVendorCreditGLItemEntry(vendorCredit);
|
||||
const itemsEntries = vendorCredit.entries.map(getItemEntry);
|
||||
|
||||
return [payableEntry, ...itemsEntries];
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverts the vendor credit associated GL entries.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorCreditId
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public revertVendorCreditGLEntries = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> => {
|
||||
await this.ledgerStorage.deleteByReference(
|
||||
tenantId,
|
||||
vendorCreditId,
|
||||
'VendorCredit',
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates vendor credit associated GL entries.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorCreditId
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public writeVendorCreditGLEntries = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||
const { VendorCredit } = this.tenancy.models(tenantId);
|
||||
|
||||
// Vendor credit with entries items.
|
||||
const vendorCredit = await VendorCredit.query(trx)
|
||||
.findById(vendorCreditId)
|
||||
.withGraphFetched('entries.item');
|
||||
|
||||
// Retrieve the payable account (A/P) account.
|
||||
const APAccount = await accountRepository.findOrCreateAccountsPayable(
|
||||
vendorCredit.currencyCode,
|
||||
{},
|
||||
trx
|
||||
);
|
||||
// Saves the vendor credit GL entries.
|
||||
const ledgerEntries = this.getVendorCreditGLEntries(
|
||||
vendorCredit,
|
||||
APAccount.id
|
||||
);
|
||||
const ledger = new Ledger(ledgerEntries);
|
||||
|
||||
// Commits the ledger entries to the storage.
|
||||
await this.ledgerStorage.commit(tenantId, ledger, trx);
|
||||
};
|
||||
|
||||
/**
|
||||
* Edits vendor credit associated GL entries.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorCreditId
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public rewriteVendorCreditGLEntries = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
// Reverts the GL entries.
|
||||
await this.revertVendorCreditGLEntries(tenantId, vendorCreditId, trx);
|
||||
|
||||
// Re-write the GL entries.
|
||||
await this.writeVendorCreditGLEntries(tenantId, vendorCreditId, trx);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import events from '@/subscribers/events';
|
||||
import {
|
||||
IVendorCreditCreatedPayload,
|
||||
IVendorCreditDeletedPayload,
|
||||
IVendorCreditEditedPayload,
|
||||
IVendorCreditOpenedPayload,
|
||||
} from '@/interfaces';
|
||||
import VendorCreditGLEntries from './VendorCreditGLEntries';
|
||||
|
||||
@Service()
|
||||
export default class VendorCreditGlEntriesSubscriber {
|
||||
@Inject()
|
||||
private vendorCreditGLEntries: VendorCreditGLEntries;
|
||||
|
||||
/***
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onCreated,
|
||||
this.writeGLEntriesOnceVendorCreditCreated
|
||||
);
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onOpened,
|
||||
this.writeGLEntgriesOnceVendorCreditOpened
|
||||
);
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onEdited,
|
||||
this.editGLEntriesOnceVendorCreditEdited
|
||||
);
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onDeleted,
|
||||
this.revertGLEntriesOnceDeleted
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes GL entries of vendor credit once the transaction created.
|
||||
* @param {IVendorCreditCreatedPayload} payload -
|
||||
*/
|
||||
private writeGLEntriesOnceVendorCreditCreated = async ({
|
||||
tenantId,
|
||||
vendorCredit,
|
||||
trx,
|
||||
}: IVendorCreditCreatedPayload): Promise<void> => {
|
||||
// Can't continue if the vendor credit is not open yet.
|
||||
if (!vendorCredit.isPublished) return;
|
||||
|
||||
await this.vendorCreditGLEntries.writeVendorCreditGLEntries(
|
||||
tenantId,
|
||||
vendorCredit.id,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes Gl entries of vendor credit once the transaction opened.
|
||||
* @param {IVendorCreditOpenedPayload} payload -
|
||||
*/
|
||||
private writeGLEntgriesOnceVendorCreditOpened = async ({
|
||||
tenantId,
|
||||
vendorCreditId,
|
||||
trx,
|
||||
}: IVendorCreditOpenedPayload) => {
|
||||
await this.vendorCreditGLEntries.writeVendorCreditGLEntries(
|
||||
tenantId,
|
||||
vendorCreditId,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Edits assocaited GL entries once vendor credit edited.
|
||||
* @param {IVendorCreditEditedPayload} payload
|
||||
*/
|
||||
private editGLEntriesOnceVendorCreditEdited = async ({
|
||||
tenantId,
|
||||
vendorCreditId,
|
||||
vendorCredit,
|
||||
trx,
|
||||
}: IVendorCreditEditedPayload) => {
|
||||
// Can't continue if the vendor credit is not open yet.
|
||||
if (!vendorCredit.isPublished) return;
|
||||
|
||||
await this.vendorCreditGLEntries.rewriteVendorCreditGLEntries(
|
||||
tenantId,
|
||||
vendorCreditId,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverts the GL entries once vendor credit deleted.
|
||||
* @param {IVendorCreditDeletedPayload} payload -
|
||||
*/
|
||||
private revertGLEntriesOnceDeleted = async ({
|
||||
vendorCreditId,
|
||||
tenantId,
|
||||
oldVendorCredit,
|
||||
}: IVendorCreditDeletedPayload): Promise<void> => {
|
||||
// Can't continue of the vendor credit is not open yet.
|
||||
if (!oldVendorCredit.isPublished) return;
|
||||
|
||||
await this.vendorCreditGLEntries.revertVendorCreditGLEntries(
|
||||
tenantId,
|
||||
vendorCreditId
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { IVendorCredit } from '@/interfaces';
|
||||
import InventoryService from '@/services/Inventory/Inventory';
|
||||
import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
|
||||
|
||||
@Service()
|
||||
export default class VendorCreditInventoryTransactions {
|
||||
@Inject()
|
||||
inventoryService: InventoryService;
|
||||
|
||||
@Inject()
|
||||
itemsEntriesService: ItemsEntriesService;
|
||||
|
||||
/**
|
||||
* Creates vendor credit associated inventory transactions.
|
||||
* @param {number} tenantId
|
||||
* @param {IVnedorCredit} vendorCredit
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public createInventoryTransactions = async (
|
||||
tenantId: number,
|
||||
vendorCredit: IVendorCredit,
|
||||
trx: Knex.Transaction
|
||||
): Promise<void> => {
|
||||
// Loads the inventory items entries of the given sale invoice.
|
||||
const inventoryEntries =
|
||||
await this.itemsEntriesService.filterInventoryEntries(
|
||||
tenantId,
|
||||
vendorCredit.entries
|
||||
);
|
||||
|
||||
const transaction = {
|
||||
transactionId: vendorCredit.id,
|
||||
transactionType: 'VendorCredit',
|
||||
transactionNumber: vendorCredit.vendorCreditNumber,
|
||||
exchangeRate: vendorCredit.exchangeRate,
|
||||
date: vendorCredit.vendorCreditDate,
|
||||
direction: 'OUT',
|
||||
entries: inventoryEntries,
|
||||
warehouseId: vendorCredit.warehouseId,
|
||||
createdAt: vendorCredit.createdAt,
|
||||
};
|
||||
// Writes inventory tranactions.
|
||||
await this.inventoryService.recordInventoryTransactionsFromItemsEntries(
|
||||
tenantId,
|
||||
transaction,
|
||||
false,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Edits vendor credit assocaited inventory transactions.
|
||||
* @param {number} tenantId
|
||||
* @param {number} creditNoteId
|
||||
* @param {ICreditNote} creditNote
|
||||
* @param {Knex.Transactions} trx
|
||||
*/
|
||||
public editInventoryTransactions = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number,
|
||||
vendorCredit: IVendorCredit,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> => {
|
||||
// Deletes inventory transactions.
|
||||
await this.deleteInventoryTransactions(tenantId, vendorCreditId, trx);
|
||||
|
||||
// Re-write inventory transactions.
|
||||
await this.createInventoryTransactions(tenantId, vendorCredit, trx);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes credit note associated inventory transactions.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} creditNoteId - Credit note id.
|
||||
* @param {Knex.Transaction} trx -
|
||||
*/
|
||||
public deleteInventoryTransactions = async (
|
||||
tenantId: number,
|
||||
vendorCreditId: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> => {
|
||||
// Deletes the inventory transactions by the given reference id and type.
|
||||
await this.inventoryService.deleteInventoryTransactions(
|
||||
tenantId,
|
||||
vendorCreditId,
|
||||
'VendorCredit',
|
||||
trx
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import events from '@/subscribers/events';
|
||||
import {
|
||||
IVendorCreditCreatedPayload,
|
||||
IVendorCreditDeletedPayload,
|
||||
IVendorCreditEditedPayload,
|
||||
} from '@/interfaces';
|
||||
import VendorCreditInventoryTransactions from './VendorCreditInventoryTransactions';
|
||||
|
||||
@Service()
|
||||
export default class VendorCreditInventoryTransactionsSubscriber {
|
||||
@Inject()
|
||||
inventoryTransactions: VendorCreditInventoryTransactions;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
* @param bus
|
||||
*/
|
||||
attach(bus) {
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onCreated,
|
||||
this.writeInventoryTransactionsOnceCreated
|
||||
);
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onEdited,
|
||||
this.rewriteInventroyTransactionsOnceEdited
|
||||
);
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onDeleted,
|
||||
this.revertInventoryTransactionsOnceDeleted
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes inventory transactions once vendor created created.
|
||||
* @param {IVendorCreditCreatedPayload} payload -
|
||||
*/
|
||||
private writeInventoryTransactionsOnceCreated = async ({
|
||||
tenantId,
|
||||
vendorCredit,
|
||||
trx,
|
||||
}: IVendorCreditCreatedPayload) => {
|
||||
await this.inventoryTransactions.createInventoryTransactions(
|
||||
tenantId,
|
||||
vendorCredit,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Rewrites inventory transactions once vendor credit edited.
|
||||
* @param {IVendorCreditEditedPayload} payload -
|
||||
*/
|
||||
private rewriteInventroyTransactionsOnceEdited = async ({
|
||||
tenantId,
|
||||
vendorCreditId,
|
||||
vendorCredit,
|
||||
trx,
|
||||
}: IVendorCreditEditedPayload) => {
|
||||
await this.inventoryTransactions.editInventoryTransactions(
|
||||
tenantId,
|
||||
vendorCreditId,
|
||||
vendorCredit,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverts inventory transactions once vendor credit deleted.
|
||||
* @param {IVendorCreditDeletedPayload} payload -
|
||||
*/
|
||||
private revertInventoryTransactionsOnceDeleted = async ({
|
||||
tenantId,
|
||||
vendorCreditId,
|
||||
trx,
|
||||
}: IVendorCreditDeletedPayload) => {
|
||||
await this.inventoryTransactions.deleteInventoryTransactions(
|
||||
tenantId,
|
||||
vendorCreditId,
|
||||
trx
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
import { formatNumber } from 'utils';
|
||||
|
||||
export class VendorCreditTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to vendor credit object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'formattedVendorCreditDate',
|
||||
'formattedAmount',
|
||||
'formattedCreditsRemaining',
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted vendor credit date.
|
||||
* @param {IVendorCredit} credit
|
||||
* @returns {String}
|
||||
*/
|
||||
protected formattedVendorCreditDate = (vendorCredit): string => {
|
||||
return this.formatDate(vendorCredit.vendorCreditDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted vendor credit amount.
|
||||
* @param {IVendorCredit} credit
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedAmount = (vendorCredit): string => {
|
||||
return formatNumber(vendorCredit.amount, {
|
||||
currencyCode: vendorCredit.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted credits remaining.
|
||||
* @param {IVendorCredit} credit
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedCreditsRemaining = (credit) => {
|
||||
return formatNumber(credit.creditsRemaining, {
|
||||
currencyCode: credit.currencyCode,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
export const ERRORS = {
|
||||
VENDOR_CREDIT_NOT_FOUND: 'VENDOR_CREDIT_NOT_FOUND',
|
||||
VENDOR_CREDIT_ALREADY_OPENED: 'VENDOR_CREDIT_ALREADY_OPENED',
|
||||
VENDOR_CREDIT_HAS_NO_REMAINING_AMOUNT: 'VENDOR_CREDIT_HAS_NO_REMAINING_AMOUNT',
|
||||
VENDOR_CREDIT_APPLY_TO_BILLS_NOT_FOUND: 'VENDOR_CREDIT_APPLY_TO_BILLS_NOT_FOUND',
|
||||
BILLS_HAS_NO_REMAINING_AMOUNT: 'BILLS_HAS_NO_REMAINING_AMOUNT',
|
||||
VENDOR_CREDIT_HAS_REFUND_TRANSACTIONS: 'VENDOR_CREDIT_HAS_REFUND_TRANSACTIONS',
|
||||
VENDOR_CREDIT_HAS_APPLIED_BILLS: 'VENDOR_CREDIT_HAS_APPLIED_BILLS'
|
||||
};
|
||||
|
||||
export const DEFAULT_VIEW_COLUMNS = [];
|
||||
export const DEFAULT_VIEWS = [
|
||||
{
|
||||
name: 'vendor_credit.view.draft',
|
||||
slug: 'draft',
|
||||
rolesLogicExpression: '1',
|
||||
roles: [
|
||||
{ index: 1, fieldKey: 'status', comparator: 'equals', value: 'draft' },
|
||||
],
|
||||
columns: DEFAULT_VIEW_COLUMNS,
|
||||
},
|
||||
{
|
||||
name: 'vendor_credit.view.published',
|
||||
slug: 'published',
|
||||
rolesLogicExpression: '1',
|
||||
roles: [
|
||||
{
|
||||
index: 1,
|
||||
fieldKey: 'status',
|
||||
comparator: 'equals',
|
||||
value: 'published',
|
||||
},
|
||||
],
|
||||
columns: DEFAULT_VIEW_COLUMNS,
|
||||
},
|
||||
{
|
||||
name: 'vendor_credit.view.open',
|
||||
slug: 'open',
|
||||
rolesLogicExpression: '1',
|
||||
roles: [
|
||||
{
|
||||
index: 1,
|
||||
fieldKey: 'status',
|
||||
comparator: 'equals',
|
||||
value: 'open',
|
||||
},
|
||||
],
|
||||
columns: DEFAULT_VIEW_COLUMNS,
|
||||
},
|
||||
{
|
||||
name: 'vendor_credit.view.closed',
|
||||
slug: 'closed',
|
||||
rolesLogicExpression: '1',
|
||||
roles: [
|
||||
{
|
||||
index: 1,
|
||||
fieldKey: 'status',
|
||||
comparator: 'equals',
|
||||
value: 'closed',
|
||||
},
|
||||
],
|
||||
columns: DEFAULT_VIEW_COLUMNS,
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user