diff --git a/server/src/api/controllers/Sales/SalesEstimates.ts b/server/src/api/controllers/Sales/SalesEstimates.ts index 211dfc16f..b6d03b654 100644 --- a/server/src/api/controllers/Sales/SalesEstimates.ts +++ b/server/src/api/controllers/Sales/SalesEstimates.ts @@ -7,7 +7,6 @@ import asyncMiddleware from 'api/middleware/asyncMiddleware'; import SaleEstimateService from 'services/Sales/SalesEstimate'; import DynamicListingService from 'services/DynamicListing/DynamicListService'; import { ServiceError } from "exceptions"; -import { Request } from 'express-validator/src/base'; @Service() export default class SalesEstimatesController extends BaseController { diff --git a/server/src/api/controllers/Sales/SalesInvoices.ts b/server/src/api/controllers/Sales/SalesInvoices.ts index 7951a91cc..ff8216b66 100644 --- a/server/src/api/controllers/Sales/SalesInvoices.ts +++ b/server/src/api/controllers/Sales/SalesInvoices.ts @@ -400,6 +400,20 @@ export default class SaleInvoicesController extends BaseController { ], }); } + if (error.errorType === 'SALE_ESTIMATE_NOT_FOUND') { + return res.boom.badRequest(null, { + errors: [ + { type: 'FROM_SALE_ESTIMATE_NOT_FOUND', code: 1200 }, + ], + }); + } + if (error.errorType === 'SALE_ESTIMATE_CONVERTED_TO_INVOICE') { + return res.boom.badRequest(null, { + errors: [ + { type: 'SALE_ESTIMATE_IS_ALREADY_CONVERTED_TO_INVOICE', code: 1300 }, + ], + }); + } } next(error); } diff --git a/server/src/interfaces/SaleEstimate.ts b/server/src/interfaces/SaleEstimate.ts index 5c5c7d47e..231c7ba59 100644 --- a/server/src/interfaces/SaleEstimate.ts +++ b/server/src/interfaces/SaleEstimate.ts @@ -14,6 +14,7 @@ export interface ISaleEstimate { sendToEmail: string, createdAt?: Date, deliveredAt: string|Date, + isConvertedToInvoice: boolean }; export interface ISaleEstimateDTO { customerId: number, diff --git a/server/src/services/Sales/SalesEstimate.ts b/server/src/services/Sales/SalesEstimate.ts index 12e0971d9..561edf2cd 100644 --- a/server/src/services/Sales/SalesEstimate.ts +++ b/server/src/services/Sales/SalesEstimate.ts @@ -98,6 +98,16 @@ export default class SaleEstimateService { } } + /** + * Validates the given sale estimate not already converted to invoice. + * @param {ISaleEstimate} saleEstimate - + */ + validateEstimateNotConverted(saleEstimate: ISaleEstimate) { + if (saleEstimate.isConvertedToInvoice) { + throw new ServiceError(ERRORS.SALE_ESTIMATE_CONVERTED_TO_INVOICE); + } + } + /** * Transform DTO object ot model object. * @param {number} tenantId @@ -181,6 +191,7 @@ export default class SaleEstimateService { tenantId, saleEstimate, saleEstimateId: saleEstimate.id, + saleEstimateDTO: estimateDTO, }); return saleEstimate; diff --git a/server/src/services/Sales/SalesInvoices.ts b/server/src/services/Sales/SalesInvoices.ts index b8c6a4f72..ceff4c9a8 100644 --- a/server/src/services/Sales/SalesInvoices.ts +++ b/server/src/services/Sales/SalesInvoices.ts @@ -165,10 +165,10 @@ export default class SaleInvoicesService { ); return { - ...formatDateFields(omit(saleInvoiceDTO, ['delivered', 'entries']), [ - 'invoiceDate', - 'dueDate', - ]), + ...formatDateFields( + omit(saleInvoiceDTO, ['delivered', 'entries', 'fromEstimateId']), + ['invoiceDate', 'dueDate'] + ), // Avoid rewrite the deliver date in edit mode when already published. ...(saleInvoiceDTO.delivered && !oldSaleInvoice?.deliveredAt && { @@ -213,6 +213,15 @@ export default class SaleInvoicesService { saleInvoiceDTO.invoiceNo ); } + // Validate the from estimate id exists on the storage. + if (saleInvoiceDTO.fromEstimateId) { + const fromEstimate = await this.saleEstimatesService.getSaleEstimateOrThrowError( + tenantId, + saleInvoiceDTO.fromEstimateId + ); + // Validate the sale estimate is not already converted to invoice. + this.saleEstimatesService.validateEstimateNotConverted(fromEstimate); + } // Validate items ids existance. await this.itemsEntriesService.validateItemsIdsExistance( tenantId, @@ -232,6 +241,7 @@ export default class SaleInvoicesService { await this.eventDispatcher.dispatch(events.saleInvoice.onCreated, { tenantId, saleInvoice, + saleInvoiceDTO, saleInvoiceId: saleInvoice.id, authorizedUser, }); @@ -503,8 +513,8 @@ export default class SaleInvoicesService { /** * Retrieve sale invoice with associated entries. - * @param {Number} saleInvoiceId - - * @param {ISystemUser} authorizedUser - + * @param {Number} saleInvoiceId - + * @param {ISystemUser} authorizedUser - * @return {Promise} */ public async getSaleInvoice( @@ -584,7 +594,6 @@ export default class SaleInvoicesService { } }); - return salesInvoices; } } diff --git a/server/src/subscribers/SaleInvoices/index.ts b/server/src/subscribers/SaleInvoices/index.ts index a6ee2848e..72e93d270 100644 --- a/server/src/subscribers/SaleInvoices/index.ts +++ b/server/src/subscribers/SaleInvoices/index.ts @@ -26,15 +26,16 @@ export default class SaleInvoiceSubscriber { * Marks the sale estimate as converted from the sale invoice once created. */ @On(events.saleInvoice.onCreated) - public async handleMarkEstimateConvert({ + public async handleMarkEstimateConvertOnceInvoiceCreated({ tenantId, saleInvoice, + saleInvoiceDTO, saleInvoiceId, }) { - if (saleInvoice.fromEstimateId) { - this.saleEstimatesService.convertEstimateToInvoice( + if (saleInvoiceDTO.fromEstimateId) { + await this.saleEstimatesService.convertEstimateToInvoice( tenantId, - saleInvoice.fromEstiamteId, + saleInvoiceDTO.fromEstimateId, saleInvoiceId ); }