mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 12:50:38 +00:00
refactor: split the services to multiple service classes (#202)
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import {
|
||||
ISaleEstimateApprovedEvent,
|
||||
ISaleEstimateApprovingEvent,
|
||||
} from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { ERRORS } from './constants';
|
||||
import { Knex } from 'knex';
|
||||
import events from '@/subscribers/events';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import moment from 'moment';
|
||||
|
||||
@Service()
|
||||
export class ApproveSaleEstimate {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
/**
|
||||
* Mark the sale estimate as approved from the customer.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
*/
|
||||
public async approveSaleEstimate(
|
||||
tenantId: number,
|
||||
saleEstimateId: number
|
||||
): Promise<void> {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve details of the given sale estimate id.
|
||||
const oldSaleEstimate = await SaleEstimate.query()
|
||||
.findById(saleEstimateId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Throws error in case the sale estimate still not delivered to customer.
|
||||
if (!oldSaleEstimate.isDelivered) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_NOT_DELIVERED);
|
||||
}
|
||||
// Throws error in case the sale estimate already approved.
|
||||
if (oldSaleEstimate.isApproved) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_ALREADY_APPROVED);
|
||||
}
|
||||
// Triggers `onSaleEstimateApproving` event.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onSaleEstimateApproving` event.
|
||||
await this.eventPublisher.emitAsync(events.saleEstimate.onApproving, {
|
||||
trx,
|
||||
tenantId,
|
||||
oldSaleEstimate,
|
||||
} as ISaleEstimateApprovingEvent);
|
||||
|
||||
// Update estimate as approved.
|
||||
const saleEstimate = await SaleEstimate.query(trx)
|
||||
.where('id', saleEstimateId)
|
||||
.patch({
|
||||
approvedAt: moment().toMySqlDateTime(),
|
||||
rejectedAt: null,
|
||||
});
|
||||
// Triggers `onSaleEstimateApproved` event.
|
||||
await this.eventPublisher.emitAsync(events.saleEstimate.onApproved, {
|
||||
trx,
|
||||
tenantId,
|
||||
oldSaleEstimate,
|
||||
saleEstimate,
|
||||
} as ISaleEstimateApprovedEvent);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import events from '@/subscribers/events';
|
||||
import { Knex } from 'knex';
|
||||
import moment from 'moment';
|
||||
import { Inject, Service } from 'typedi';
|
||||
|
||||
@Service()
|
||||
export class ConvertSaleEstimate {
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Converts estimate to invoice.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} estimateId -
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async convertEstimateToInvoice(
|
||||
tenantId: number,
|
||||
estimateId: number,
|
||||
invoiceId: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve details of the given sale estimate.
|
||||
const saleEstimate = await SaleEstimate.query()
|
||||
.findById(estimateId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Marks the estimate as converted from the givne invoice.
|
||||
await SaleEstimate.query(trx).where('id', estimateId).patch({
|
||||
convertedToInvoiceId: invoiceId,
|
||||
convertedToInvoiceAt: moment().toMySqlDateTime(),
|
||||
});
|
||||
// Triggers `onSaleEstimateConvertedToInvoice` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.saleEstimate.onConvertedToInvoice,
|
||||
{}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import {
|
||||
ISaleEstimate,
|
||||
ISaleEstimateCreatedPayload,
|
||||
ISaleEstimateCreatingPayload,
|
||||
ISaleEstimateDTO,
|
||||
} from '@/interfaces';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { SaleEstimateDTOTransformer } from './SaleEstimateDTOTransformer';
|
||||
import events from '@/subscribers/events';
|
||||
import { SaleEstimateValidators } from './SaleEstimateValidators';
|
||||
|
||||
@Service()
|
||||
export class CreateSaleEstimate {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private itemsEntriesService: ItemsEntriesService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private transformerDTO: SaleEstimateDTOTransformer;
|
||||
|
||||
@Inject()
|
||||
private validators: SaleEstimateValidators;
|
||||
|
||||
/**
|
||||
* Creates a new estimate with associated entries.
|
||||
* @async
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {EstimateDTO} estimate
|
||||
* @return {Promise<ISaleEstimate>}
|
||||
*/
|
||||
public async createEstimate(
|
||||
tenantId: number,
|
||||
estimateDTO: ISaleEstimateDTO
|
||||
): Promise<ISaleEstimate> {
|
||||
const { SaleEstimate, Contact } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the given customer or throw not found service error.
|
||||
const customer = await Contact.query()
|
||||
.modify('customer')
|
||||
.findById(estimateDTO.customerId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Transform DTO object ot model object.
|
||||
const estimateObj = await this.transformerDTO.transformDTOToModel(
|
||||
tenantId,
|
||||
estimateDTO,
|
||||
customer
|
||||
);
|
||||
// Validate estimate number uniquiness on the storage.
|
||||
await this.validators.validateEstimateNumberExistance(
|
||||
tenantId,
|
||||
estimateObj.estimateNumber
|
||||
);
|
||||
// Validate items IDs existance on the storage.
|
||||
await this.itemsEntriesService.validateItemsIdsExistance(
|
||||
tenantId,
|
||||
estimateDTO.entries
|
||||
);
|
||||
// Validate non-sellable items.
|
||||
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
||||
tenantId,
|
||||
estimateDTO.entries
|
||||
);
|
||||
// Creates a sale estimate transaction with associated transactions as UOW.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onSaleEstimateCreating` event.
|
||||
await this.eventPublisher.emitAsync(events.saleEstimate.onCreating, {
|
||||
estimateDTO,
|
||||
tenantId,
|
||||
trx,
|
||||
} as ISaleEstimateCreatingPayload);
|
||||
|
||||
// Upsert the sale estimate graph to the storage.
|
||||
const saleEstimate = await SaleEstimate.query(trx).upsertGraphAndFetch({
|
||||
...estimateObj,
|
||||
});
|
||||
// Triggers `onSaleEstimateCreated` event.
|
||||
await this.eventPublisher.emitAsync(events.saleEstimate.onCreated, {
|
||||
tenantId,
|
||||
saleEstimate,
|
||||
saleEstimateId: saleEstimate.id,
|
||||
saleEstimateDTO: estimateDTO,
|
||||
trx,
|
||||
} as ISaleEstimateCreatedPayload);
|
||||
|
||||
return saleEstimate;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import {
|
||||
ISaleEstimateDeletedPayload,
|
||||
ISaleEstimateDeletingPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { ERRORS } from './constants';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { Knex } from 'knex';
|
||||
|
||||
@Service()
|
||||
export class DeleteSaleEstimate {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
/**
|
||||
* Deletes the given estimate id with associated entries.
|
||||
* @async
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {IEstimate} estimateId
|
||||
* @return {void}
|
||||
*/
|
||||
public async deleteEstimate(
|
||||
tenantId: number,
|
||||
estimateId: number
|
||||
): Promise<void> {
|
||||
const { SaleEstimate, ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve sale estimate or throw not found service error.
|
||||
const oldSaleEstimate = await SaleEstimate.query()
|
||||
.findById(estimateId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Throw error if the sale estimate converted to sale invoice.
|
||||
if (oldSaleEstimate.convertedToInvoiceId) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_CONVERTED_TO_INVOICE);
|
||||
}
|
||||
// Deletes the estimate with associated transactions under UOW enivrement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onSaleEstimatedDeleting` event.
|
||||
await this.eventPublisher.emitAsync(events.saleEstimate.onDeleting, {
|
||||
trx,
|
||||
tenantId,
|
||||
oldSaleEstimate,
|
||||
} as ISaleEstimateDeletingPayload);
|
||||
|
||||
// Delete sale estimate entries.
|
||||
await ItemEntry.query(trx)
|
||||
.where('reference_id', estimateId)
|
||||
.where('reference_type', 'SaleEstimate')
|
||||
.delete();
|
||||
|
||||
// Delete sale estimate transaction.
|
||||
await SaleEstimate.query(trx).where('id', estimateId).delete();
|
||||
|
||||
// Triggers `onSaleEstimatedDeleted` event.
|
||||
await this.eventPublisher.emitAsync(events.saleEstimate.onDeleted, {
|
||||
tenantId,
|
||||
saleEstimateId: estimateId,
|
||||
oldSaleEstimate,
|
||||
trx,
|
||||
} as ISaleEstimateDeletedPayload);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import {
|
||||
ISaleEstimateEventDeliveredPayload,
|
||||
ISaleEstimateEventDeliveringPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { ERRORS } from './constants';
|
||||
import moment from 'moment';
|
||||
|
||||
@Service()
|
||||
export class DeliverSaleEstimate {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
/**
|
||||
* Mark the sale estimate as delivered.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} saleEstimateId - Sale estimate id.
|
||||
*/
|
||||
public async deliverSaleEstimate(
|
||||
tenantId: number,
|
||||
saleEstimateId: number
|
||||
): Promise<void> {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve details of the given sale estimate id.
|
||||
const oldSaleEstimate = await SaleEstimate.query()
|
||||
.findById(saleEstimateId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Throws error in case the sale estimate already published.
|
||||
if (oldSaleEstimate.isDelivered) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_ALREADY_DELIVERED);
|
||||
}
|
||||
// Updates the sale estimate transaction with assocaited transactions
|
||||
// under UOW envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onSaleEstimateDelivering` event.
|
||||
await this.eventPublisher.emitAsync(events.saleEstimate.onDelivering, {
|
||||
oldSaleEstimate,
|
||||
trx,
|
||||
tenantId,
|
||||
} as ISaleEstimateEventDeliveringPayload);
|
||||
|
||||
// Record the delivered at on the storage.
|
||||
const saleEstimate = await SaleEstimate.query(trx).patchAndFetchById(
|
||||
saleEstimateId,
|
||||
{
|
||||
deliveredAt: moment().toMySqlDateTime(),
|
||||
}
|
||||
);
|
||||
// Triggers `onSaleEstimateDelivered` event.
|
||||
await this.eventPublisher.emitAsync(events.saleEstimate.onDelivered, {
|
||||
tenantId,
|
||||
saleEstimate,
|
||||
trx,
|
||||
} as ISaleEstimateEventDeliveredPayload);
|
||||
});
|
||||
}
|
||||
}
|
||||
123
packages/server/src/services/Sales/Estimates/EditSaleEstimate.ts
Normal file
123
packages/server/src/services/Sales/Estimates/EditSaleEstimate.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import {
|
||||
ISaleEstimate,
|
||||
ISaleEstimateDTO,
|
||||
ISaleEstimateEditedPayload,
|
||||
ISaleEstimateEditingPayload,
|
||||
} from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { SaleEstimateValidators } from './SaleEstimateValidators';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import { SaleEstimateDTOTransformer } from './SaleEstimateDTOTransformer';
|
||||
import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
|
||||
import events from '@/subscribers/events';
|
||||
|
||||
@Service()
|
||||
export class EditSaleEstimate {
|
||||
@Inject()
|
||||
private validators: SaleEstimateValidators;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private itemsEntriesService: ItemsEntriesService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private transformerDTO: SaleEstimateDTOTransformer;
|
||||
|
||||
/**
|
||||
* Edit details of the given estimate with associated entries.
|
||||
* @async
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {Integer} estimateId
|
||||
* @param {EstimateDTO} estimate
|
||||
* @return {Promise<ISaleEstimate>}
|
||||
*/
|
||||
public async editEstimate(
|
||||
tenantId: number,
|
||||
estimateId: number,
|
||||
estimateDTO: ISaleEstimateDTO
|
||||
): Promise<ISaleEstimate> {
|
||||
const { SaleEstimate, Contact } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve details of the given sale estimate id.
|
||||
const oldSaleEstimate = await SaleEstimate.query().findById(estimateId);
|
||||
|
||||
// Validates the given estimate existance.
|
||||
this.validators.validateEstimateExistance(oldSaleEstimate);
|
||||
|
||||
// Retrieve the given customer or throw not found service error.
|
||||
const customer = await Contact.query()
|
||||
.modify('customer')
|
||||
.findById(estimateDTO.customerId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Transform DTO object ot model object.
|
||||
const estimateObj = await this.transformerDTO.transformDTOToModel(
|
||||
tenantId,
|
||||
estimateDTO,
|
||||
oldSaleEstimate,
|
||||
customer
|
||||
);
|
||||
// Validate estimate number uniquiness on the storage.
|
||||
if (estimateDTO.estimateNumber) {
|
||||
await this.validators.validateEstimateNumberExistance(
|
||||
tenantId,
|
||||
estimateDTO.estimateNumber,
|
||||
estimateId
|
||||
);
|
||||
}
|
||||
// Validate sale estimate entries existance.
|
||||
await this.itemsEntriesService.validateEntriesIdsExistance(
|
||||
tenantId,
|
||||
estimateId,
|
||||
'SaleEstimate',
|
||||
estimateDTO.entries
|
||||
);
|
||||
// Validate items IDs existance on the storage.
|
||||
await this.itemsEntriesService.validateItemsIdsExistance(
|
||||
tenantId,
|
||||
estimateDTO.entries
|
||||
);
|
||||
// Validate non-sellable items.
|
||||
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
||||
tenantId,
|
||||
estimateDTO.entries
|
||||
);
|
||||
// Edits estimate transaction with associated transactions
|
||||
// under unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx) => {
|
||||
// Trigger `onSaleEstimateEditing` event.
|
||||
await this.eventPublisher.emitAsync(events.saleEstimate.onEditing, {
|
||||
tenantId,
|
||||
oldSaleEstimate,
|
||||
estimateDTO,
|
||||
trx,
|
||||
} as ISaleEstimateEditingPayload);
|
||||
|
||||
// Upsert the estimate graph to the storage.
|
||||
const saleEstimate = await SaleEstimate.query(trx).upsertGraphAndFetch({
|
||||
id: estimateId,
|
||||
...estimateObj,
|
||||
});
|
||||
// Trigger `onSaleEstimateEdited` event.
|
||||
await this.eventPublisher.emitAsync(events.saleEstimate.onEdited, {
|
||||
tenantId,
|
||||
estimateId,
|
||||
saleEstimate,
|
||||
oldSaleEstimate,
|
||||
trx,
|
||||
} as ISaleEstimateEditedPayload);
|
||||
|
||||
return saleEstimate;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
import { SaleEstimateTransfromer } from './SaleEstimateTransformer';
|
||||
import { SaleEstimateValidators } from './SaleEstimateValidators';
|
||||
|
||||
@Service()
|
||||
export class GetSaleEstimate {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
@Inject()
|
||||
private validators: SaleEstimateValidators;
|
||||
|
||||
/**
|
||||
* Retrieve the estimate details with associated entries.
|
||||
* @async
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {Integer} estimateId
|
||||
*/
|
||||
public async getEstimate(tenantId: number, estimateId: number) {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
const estimate = await SaleEstimate.query()
|
||||
.findById(estimateId)
|
||||
.withGraphFetched('entries.item')
|
||||
.withGraphFetched('customer')
|
||||
.withGraphFetched('branch');
|
||||
|
||||
// Validates the estimate existance.
|
||||
this.validators.validateEstimateExistance(estimate);
|
||||
|
||||
// Transformes sale estimate model to POJO.
|
||||
return this.transformer.transform(
|
||||
tenantId,
|
||||
estimate,
|
||||
new SaleEstimateTransfromer()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import * as R from 'ramda';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import {
|
||||
IFilterMeta,
|
||||
IPaginationMeta,
|
||||
ISaleEstimate,
|
||||
ISalesEstimatesFilter,
|
||||
} from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
import { SaleEstimateDTOTransformer } from './SaleEstimateDTOTransformer';
|
||||
import { SaleEstimateTransfromer } from './SaleEstimateTransformer';
|
||||
|
||||
@Service()
|
||||
export class GetSaleEstimates {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private dynamicListService: DynamicListingService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Retrieves estimates filterable and paginated list.
|
||||
* @param {number} tenantId -
|
||||
* @param {IEstimatesFilter} estimatesFilter -
|
||||
*/
|
||||
public async getEstimates(
|
||||
tenantId: number,
|
||||
filterDTO: ISalesEstimatesFilter
|
||||
): Promise<{
|
||||
salesEstimates: ISaleEstimate[];
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses filter DTO.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
SaleEstimate,
|
||||
filter
|
||||
);
|
||||
const { results, pagination } = await SaleEstimate.query()
|
||||
.onBuild((builder) => {
|
||||
builder.withGraphFetched('customer');
|
||||
builder.withGraphFetched('entries');
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
})
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
const transformedEstimates = await this.transformer.transform(
|
||||
tenantId,
|
||||
results,
|
||||
new SaleEstimateTransfromer()
|
||||
);
|
||||
return {
|
||||
salesEstimates: transformedEstimates,
|
||||
pagination,
|
||||
filterMeta: dynamicFilter.getResponseMeta(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the sale receipts list filter DTO.
|
||||
* @param filterDTO
|
||||
*/
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import moment from 'moment';
|
||||
import { Knex } from 'knex';
|
||||
import events from '@/subscribers/events';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { ERRORS } from './constants';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
|
||||
@Service()
|
||||
export class RejectSaleEstimate {
|
||||
@Inject()
|
||||
private tenancy: TenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
/**
|
||||
* Mark the sale estimate as rejected from the customer.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
*/
|
||||
public async rejectSaleEstimate(
|
||||
tenantId: number,
|
||||
saleEstimateId: number
|
||||
): Promise<void> {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve details of the given sale estimate id.
|
||||
const saleEstimate = await SaleEstimate.query()
|
||||
.findById(saleEstimateId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Throws error in case the sale estimate still not delivered to customer.
|
||||
if (!saleEstimate.isDelivered) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_NOT_DELIVERED);
|
||||
}
|
||||
// Throws error in case the sale estimate already rejected.
|
||||
if (saleEstimate.isRejected) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_ALREADY_REJECTED);
|
||||
}
|
||||
//
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Mark the sale estimate as reject on the storage.
|
||||
await SaleEstimate.query(trx).where('id', saleEstimateId).patch({
|
||||
rejectedAt: moment().toMySqlDateTime(),
|
||||
approvedAt: null,
|
||||
});
|
||||
// Triggers `onSaleEstimateRejected` event.
|
||||
await this.eventPublisher.emitAsync(events.saleEstimate.onRejected, {});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
import * as R from 'ramda';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { omit, sumBy } from 'lodash';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { ICustomer, ISaleEstimate, ISaleEstimateDTO } from '@/interfaces';
|
||||
import { SaleEstimateValidators } from './SaleEstimateValidators';
|
||||
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
|
||||
import { WarehouseTransactionDTOTransform } from '@/services/Warehouses/Integrations/WarehouseTransactionDTOTransform';
|
||||
import { formatDateFields } from '@/utils';
|
||||
import moment from 'moment';
|
||||
import { SaleEstimateIncrement } from './SaleEstimateIncrement';
|
||||
|
||||
@Service()
|
||||
export class SaleEstimateDTOTransformer {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private validators: SaleEstimateValidators;
|
||||
|
||||
@Inject()
|
||||
private branchDTOTransform: BranchTransactionDTOTransform;
|
||||
|
||||
@Inject()
|
||||
private warehouseDTOTransform: WarehouseTransactionDTOTransform;
|
||||
|
||||
@Inject()
|
||||
private estimateIncrement: SaleEstimateIncrement;
|
||||
|
||||
/**
|
||||
* Transform create DTO object ot model object.
|
||||
* @param {number} tenantId
|
||||
* @param {ISaleEstimateDTO} saleEstimateDTO - Sale estimate DTO.
|
||||
* @return {ISaleEstimate}
|
||||
*/
|
||||
async transformDTOToModel(
|
||||
tenantId: number,
|
||||
estimateDTO: ISaleEstimateDTO,
|
||||
paymentCustomer: ICustomer,
|
||||
oldSaleEstimate?: ISaleEstimate
|
||||
): Promise<ISaleEstimate> {
|
||||
const { ItemEntry, Contact } = this.tenancy.models(tenantId);
|
||||
|
||||
const amount = sumBy(estimateDTO.entries, (e) => ItemEntry.calcAmount(e));
|
||||
|
||||
// Retreive the next invoice number.
|
||||
const autoNextNumber =
|
||||
this.estimateIncrement.getNextEstimateNumber(tenantId);
|
||||
|
||||
// Retreive the next estimate number.
|
||||
const estimateNumber =
|
||||
estimateDTO.estimateNumber ||
|
||||
oldSaleEstimate?.estimateNumber ||
|
||||
autoNextNumber;
|
||||
|
||||
// Validate the sale estimate number require.
|
||||
this.validators.validateEstimateNoRequire(estimateNumber);
|
||||
|
||||
const initialDTO = {
|
||||
amount,
|
||||
...formatDateFields(omit(estimateDTO, ['delivered', 'entries']), [
|
||||
'estimateDate',
|
||||
'expirationDate',
|
||||
]),
|
||||
currencyCode: paymentCustomer.currencyCode,
|
||||
exchangeRate: estimateDTO.exchangeRate || 1,
|
||||
...(estimateNumber ? { estimateNumber } : {}),
|
||||
entries: estimateDTO.entries.map((entry) => ({
|
||||
reference_type: 'SaleEstimate',
|
||||
...entry,
|
||||
})),
|
||||
// Avoid rewrite the deliver date in edit mode when already published.
|
||||
...(estimateDTO.delivered &&
|
||||
!oldSaleEstimate?.deliveredAt && {
|
||||
deliveredAt: moment().toMySqlDateTime(),
|
||||
}),
|
||||
};
|
||||
return R.compose(
|
||||
this.branchDTOTransform.transformDTO<ISaleEstimate>(tenantId),
|
||||
this.warehouseDTOTransform.transformDTO<ISaleEstimate>(tenantId)
|
||||
)(initialDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve estimate number to object model.
|
||||
* @param {number} tenantId
|
||||
* @param {ISaleEstimateDTO} saleEstimateDTO
|
||||
* @param {ISaleEstimate} oldSaleEstimate
|
||||
*/
|
||||
public transformEstimateNumberToModel(
|
||||
tenantId: number,
|
||||
saleEstimateDTO: ISaleEstimateDTO,
|
||||
oldSaleEstimate?: ISaleEstimate
|
||||
): string {
|
||||
// Retreive the next invoice number.
|
||||
const autoNextNumber =
|
||||
this.estimateIncrement.getNextEstimateNumber(tenantId);
|
||||
|
||||
if (saleEstimateDTO.estimateNumber) {
|
||||
return saleEstimateDTO.estimateNumber;
|
||||
}
|
||||
return oldSaleEstimate ? oldSaleEstimate.estimateNumber : autoNextNumber;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import AutoIncrementOrdersService from '../AutoIncrementOrdersService';
|
||||
|
||||
@Service()
|
||||
export class SaleEstimateIncrement {
|
||||
@Inject()
|
||||
private autoIncrementOrdersService: AutoIncrementOrdersService;
|
||||
|
||||
/**
|
||||
* Retrieve the next unique estimate number.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @return {string}
|
||||
*/
|
||||
public getNextEstimateNumber(tenantId: number): string {
|
||||
return this.autoIncrementOrdersService.getNextTransactionNumber(
|
||||
tenantId,
|
||||
'sales_estimates'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the estimate next number.
|
||||
* @param {number} tenantId -
|
||||
*/
|
||||
public incrementNextEstimateNumber(tenantId: number) {
|
||||
return this.autoIncrementOrdersService.incrementSettingsNextNumber(
|
||||
tenantId,
|
||||
'sales_estimates'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import events from '@/subscribers/events';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import SaleNotifyBySms from '../SaleNotifyBySms';
|
||||
import SmsNotificationsSettingsService from '@/services/Settings/SmsNotificationsSettings';
|
||||
import SMSClient from '@/services/SMSClient';
|
||||
import {
|
||||
ICustomer,
|
||||
IPaymentReceiveSmsDetails,
|
||||
@@ -21,18 +20,18 @@ const ERRORS = {
|
||||
};
|
||||
|
||||
@Service()
|
||||
export default class SaleEstimateNotifyBySms {
|
||||
export class SaleEstimateNotifyBySms {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
saleSmsNotification: SaleNotifyBySms;
|
||||
private saleSmsNotification: SaleNotifyBySms;
|
||||
|
||||
@Inject()
|
||||
eventPublisher: EventPublisher;
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
smsNotificationsSettings: SmsNotificationsSettingsService;
|
||||
private smsNotificationsSettings: SmsNotificationsSettingsService;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -187,6 +186,7 @@ export default class SaleEstimateNotifyBySms {
|
||||
.findById(saleEstimateId)
|
||||
.withGraphFetched('customer');
|
||||
|
||||
// Validates the estimate existance.
|
||||
this.validateEstimateExistance(saleEstimate);
|
||||
|
||||
// Retrieve the current tenant metadata.
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ISaleEstimate } from '@/interfaces';
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
import { formatNumber } from 'utils';
|
||||
|
||||
export default class SaleEstimateTransfromer extends Transformer {
|
||||
export class SaleEstimateTransfromer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to sale invoice object.
|
||||
* @returns {Array}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { ISaleEstimate } from '@/interfaces';
|
||||
import { ERRORS } from './constants';
|
||||
import { SaleEstimate } from '@/models';
|
||||
|
||||
@Service()
|
||||
export class SaleEstimateValidators {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Validates the given estimate existance.
|
||||
* @param {SaleEstimate | undefined | null} estimate -
|
||||
*/
|
||||
public validateEstimateExistance(estimate: SaleEstimate | undefined | null) {
|
||||
if (!estimate) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the estimate number unique on the storage.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {Function} next
|
||||
*/
|
||||
public async validateEstimateNumberExistance(
|
||||
tenantId: number,
|
||||
estimateNumber: string,
|
||||
notEstimateId?: number
|
||||
) {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
const foundSaleEstimate = await SaleEstimate.query()
|
||||
.findOne('estimate_number', estimateNumber)
|
||||
.onBuild((builder) => {
|
||||
if (notEstimateId) {
|
||||
builder.whereNot('id', notEstimateId);
|
||||
}
|
||||
});
|
||||
if (foundSaleEstimate) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_NUMBER_EXISTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given sale estimate not already converted to invoice.
|
||||
* @param {ISaleEstimate} saleEstimate -
|
||||
*/
|
||||
public validateEstimateNotConverted(saleEstimate: ISaleEstimate) {
|
||||
if (saleEstimate.isConvertedToInvoice) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_CONVERTED_TO_INVOICE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the sale estimate number require.
|
||||
* @param {ISaleEstimate} saleInvoiceObj
|
||||
*/
|
||||
public validateEstimateNoRequire(estimateNumber: string) {
|
||||
if (!estimateNumber) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_NO_IS_REQUIRED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given customer has no sales estimates.
|
||||
* @param {number} tenantId
|
||||
* @param {number} customerId - Customer id.
|
||||
*/
|
||||
public async validateCustomerHasNoEstimates(
|
||||
tenantId: number,
|
||||
customerId: number
|
||||
) {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
const estimates = await SaleEstimate.query().where(
|
||||
'customer_id',
|
||||
customerId
|
||||
);
|
||||
if (estimates.length > 0) {
|
||||
throw new ServiceError(ERRORS.CUSTOMER_HAS_SALES_ESTIMATES);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { CreateSaleEstimate } from './CreateSaleEstimate';
|
||||
import {
|
||||
IFilterMeta,
|
||||
IPaginationMeta,
|
||||
IPaymentReceiveSmsDetails,
|
||||
ISaleEstimate,
|
||||
ISaleEstimateDTO,
|
||||
ISalesEstimatesFilter,
|
||||
} from '@/interfaces';
|
||||
import { EditSaleEstimate } from './EditSaleEstimate';
|
||||
import { DeleteSaleEstimate } from './DeleteSaleEstimate';
|
||||
import { GetSaleEstimate } from './GetSaleEstimate';
|
||||
import { GetSaleEstimates } from './GetSaleEstimates';
|
||||
import { DeliverSaleEstimate } from './DeliverSaleEstimate';
|
||||
import { ApproveSaleEstimate } from './ApproveSaleEstimate';
|
||||
import { RejectSaleEstimate } from './RejectSaleEstimate';
|
||||
import { SaleEstimateNotifyBySms } from './SaleEstimateSmsNotify';
|
||||
import { SaleEstimatesPdf } from './SaleEstimatesPdf';
|
||||
|
||||
@Service()
|
||||
export class SaleEstimatesApplication {
|
||||
@Inject()
|
||||
private createSaleEstimateService: CreateSaleEstimate;
|
||||
|
||||
@Inject()
|
||||
private editSaleEstimateService: EditSaleEstimate;
|
||||
|
||||
@Inject()
|
||||
private deleteSaleEstimateService: DeleteSaleEstimate;
|
||||
|
||||
@Inject()
|
||||
private getSaleEstimateService: GetSaleEstimate;
|
||||
|
||||
@Inject()
|
||||
private getSaleEstimatesService: GetSaleEstimates;
|
||||
|
||||
@Inject()
|
||||
private deliverSaleEstimateService: DeliverSaleEstimate;
|
||||
|
||||
@Inject()
|
||||
private approveSaleEstimateService: ApproveSaleEstimate;
|
||||
|
||||
@Inject()
|
||||
private rejectSaleEstimateService: RejectSaleEstimate;
|
||||
|
||||
@Inject()
|
||||
private saleEstimateNotifyBySmsService: SaleEstimateNotifyBySms;
|
||||
|
||||
@Inject()
|
||||
private saleEstimatesPdfService: SaleEstimatesPdf;
|
||||
|
||||
/**
|
||||
* Create a sale estimate.
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {EstimateDTO} estimate
|
||||
* @return {Promise<ISaleEstimate>}
|
||||
*/
|
||||
public createSaleEstimate(
|
||||
tenantId: number,
|
||||
estimateDTO: ISaleEstimateDTO
|
||||
): Promise<ISaleEstimate> {
|
||||
return this.createSaleEstimateService.createEstimate(tenantId, estimateDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit the given sale estimate.
|
||||
* @param {number} tenantId - The tenant id.
|
||||
* @param {Integer} estimateId
|
||||
* @param {EstimateDTO} estimate
|
||||
* @return {Promise<ISaleEstimate>}
|
||||
*/
|
||||
public editSaleEstimate(
|
||||
tenantId: number,
|
||||
estimateId: number,
|
||||
estimateDTO: ISaleEstimateDTO
|
||||
): Promise<ISaleEstimate> {
|
||||
return this.editSaleEstimateService.editEstimate(
|
||||
tenantId,
|
||||
estimateId,
|
||||
estimateDTO
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given sale estimate.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} estimateId -
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public deleteSaleEstimate(
|
||||
tenantId: number,
|
||||
estimateId: number
|
||||
): Promise<void> {
|
||||
return this.deleteSaleEstimateService.deleteEstimate(tenantId, estimateId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the given sale estimate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} estimateId
|
||||
*/
|
||||
public getSaleEstimate(tenantId: number, estimateId: number) {
|
||||
return this.getSaleEstimateService.getEstimate(tenantId, estimateId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the sale estimate.
|
||||
* @param {number} tenantId
|
||||
* @param {ISalesEstimatesFilter} filterDTO
|
||||
* @returns
|
||||
*/
|
||||
public getSaleEstimates(
|
||||
tenantId: number,
|
||||
filterDTO: ISalesEstimatesFilter
|
||||
): Promise<{
|
||||
salesEstimates: ISaleEstimate[];
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
return this.getSaleEstimatesService.getEstimates(tenantId, filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deliver the given sale estimate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public deliverSaleEstimate(tenantId: number, saleEstimateId: number) {
|
||||
return this.deliverSaleEstimateService.deliverSaleEstimate(
|
||||
tenantId,
|
||||
saleEstimateId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve the given sale estimate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public approveSaleEstimate(
|
||||
tenantId: number,
|
||||
saleEstimateId: number
|
||||
): Promise<void> {
|
||||
return this.approveSaleEstimateService.approveSaleEstimate(
|
||||
tenantId,
|
||||
saleEstimateId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the sale estimate as rejected from the customer.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
*/
|
||||
public async rejectSaleEstimate(
|
||||
tenantId: number,
|
||||
saleEstimateId: number
|
||||
): Promise<void> {
|
||||
return this.rejectSaleEstimateService.rejectSaleEstimate(
|
||||
tenantId,
|
||||
saleEstimateId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the customer of the given sale estimate by SMS.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
* @returns {Promise<ISaleEstimate>}
|
||||
*/
|
||||
public notifySaleEstimateBySms = async (
|
||||
tenantId: number,
|
||||
saleEstimateId: number
|
||||
): Promise<ISaleEstimate> => {
|
||||
return this.saleEstimateNotifyBySmsService.notifyBySms(
|
||||
tenantId,
|
||||
saleEstimateId
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the SMS details of the given payment receive transaction.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleEstimateId
|
||||
* @returns {Promise<IPaymentReceiveSmsDetails>}
|
||||
*/
|
||||
public getSaleEstimateSmsDetails = (
|
||||
tenantId: number,
|
||||
saleEstimateId: number
|
||||
): Promise<IPaymentReceiveSmsDetails> => {
|
||||
return this.saleEstimateNotifyBySmsService.smsDetails(
|
||||
tenantId,
|
||||
saleEstimateId
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {} saleEstimate
|
||||
* @returns
|
||||
*/
|
||||
public getSaleEstimatePdf(tenantId: number, saleEstimate) {
|
||||
return this.saleEstimatesPdfService.getSaleEstimatePdf(
|
||||
tenantId,
|
||||
saleEstimate
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,18 +5,18 @@ import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { Tenant } from '@/system/models';
|
||||
|
||||
@Service()
|
||||
export default class SaleEstimatesPdf {
|
||||
export class SaleEstimatesPdf {
|
||||
@Inject()
|
||||
pdfService: PdfService;
|
||||
private pdfService: PdfService;
|
||||
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Retrieve sale invoice pdf content.
|
||||
* @param {} saleInvoice -
|
||||
*/
|
||||
async saleEstimatePdf(tenantId: number, saleEstimate) {
|
||||
async getSaleEstimatePdf(tenantId: number, saleEstimate) {
|
||||
const i18n = this.tenancy.i18n(tenantId);
|
||||
|
||||
const organization = await Tenant.query()
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class UnlinkConvertedSaleEstimate {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Unlink the converted sale estimates from the given sale invoice.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} invoiceId -
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async unlinkConvertedEstimateFromInvoice(
|
||||
tenantId: number,
|
||||
invoiceId: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> {
|
||||
const { SaleEstimate } = this.tenancy.models(tenantId);
|
||||
|
||||
await SaleEstimate.query(trx)
|
||||
.where({
|
||||
convertedToInvoiceId: invoiceId,
|
||||
})
|
||||
.patch({
|
||||
convertedToInvoiceId: null,
|
||||
convertedToInvoiceAt: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user