import { ArticleIdentifier } from "@/contexts/catalog/domain"; import { IUseCase, IUseCaseError, UseCaseError } from "@/contexts/common/application"; import { IRepositoryManager } from "@/contexts/common/domain"; import { IInfrastructureError } from "@/contexts/common/infrastructure"; import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize"; import { Collection, CurrencyData, Description, DomainError, ICreateQuote_Request_DTO, IDomainError, Language, Note, Percentage, Quantity, Result, TextValueObject, UTCDateValue, UniqueID, UnitPrice, ensureIdIsValid, } from "@shared/contexts"; import { Dealer, IQuoteRepository, Quote, QuoteCustomer, QuoteItem, QuoteReference, QuoteStatus, } from "../../domain"; import { ISalesContext } from "../../infrastructure"; export type CreateQuoteResponseOrError = | Result // Misc errors (value objects) | Result; // Success! export class CreateQuoteUseCase implements IUseCase> { private _adapter: ISequelizeAdapter; private _repositoryManager: IRepositoryManager; private _dealer?: Dealer; constructor(context: ISalesContext) { this._adapter = context.adapter; this._repositoryManager = context.repositoryManager; this._dealer = context.dealer; } async execute(request: ICreateQuote_Request_DTO) { const { id } = request; // Validaciones de datos if (!this._dealer) { const message = "Error. Missing Dealer"; return Result.fail(UseCaseError.create(UseCaseError.INVALID_INPUT_DATA, message)); } const dealerId = this._dealer.id; const idOrError = ensureIdIsValid(id); if (idOrError.isFailure) { const message = idOrError.error.message; //`Quote ID ${quoteDTO.id} is not valid`; return Result.fail( UseCaseError.create(UseCaseError.INVALID_INPUT_DATA, message, [{ path: "id" }]) ); } // Comprobar que no existe un quote previo con esos datos const quoteRepository = this._getQuoteRepository(); const idExists = await quoteRepository().exists(idOrError.object); if (idExists) { const message = `Another quote with same ID exists`; return Result.fail( UseCaseError.create(UseCaseError.RESOURCE_ALREADY_EXITS, message, { path: "id", }) ); } // Crear quote const quoteOrError = this._tryCreateQuoteInstance(request, idOrError.object, dealerId); if (quoteOrError.isFailure) { const { error: domainError } = quoteOrError; let errorCode = ""; let message = ""; switch (domainError.code) { case DomainError.INVALID_INPUT_DATA: errorCode = UseCaseError.INVALID_INPUT_DATA; message = "El presupuesto tiene algún dato erróneo."; break; default: errorCode = UseCaseError.UNEXCEPTED_ERROR; message = domainError.message; break; } return Result.fail(UseCaseError.create(errorCode, message, domainError)); } return this._saveQuote(quoteOrError.object); } private async _saveQuote(quote: Quote) { // Guardar el contacto const transaction = this._adapter.startTransaction(); const quoteRepository = this._getQuoteRepository(); let quoteRepo: IQuoteRepository; try { await transaction.complete(async (t) => { quoteRepo = quoteRepository({ transaction: t }); await quoteRepo.create(quote); }); return Result.ok(quote); } catch (error: unknown) { const _error = error as IInfrastructureError; return Result.fail(UseCaseError.create(UseCaseError.REPOSITORY_ERROR, _error.message)); } } private _tryCreateQuoteInstance( quoteDTO: ICreateQuote_Request_DTO, quoteId: UniqueID, dealerId: UniqueID ): Result { const statusOrError = QuoteStatus.create(quoteDTO.status); if (statusOrError.isFailure) { return Result.fail(statusOrError.error); } const dateOrError = UTCDateValue.create(quoteDTO.date); if (dateOrError.isFailure) { return Result.fail(dateOrError.error); } const referenceOrError = QuoteReference.create(quoteDTO.reference); if (referenceOrError.isFailure) { return Result.fail(referenceOrError.error); } const languageOrError = Language.createFromCode( quoteDTO.lang_code ?? this._dealer?.language.code ); if (languageOrError.isFailure) { return Result.fail(languageOrError.error); } const customerOrError = QuoteCustomer.create(quoteDTO.customer_information); if (customerOrError.isFailure) { return Result.fail(customerOrError.error); } const currencyOrError = CurrencyData.createFromCode( quoteDTO.currency_code ?? CurrencyData.DEFAULT_CURRENCY_CODE ); if (currencyOrError.isFailure) { return Result.fail(currencyOrError.error); } const paymentOrError = Note.create(quoteDTO.payment_method); if (paymentOrError.isFailure) { return Result.fail(paymentOrError.error); } const notesOrError = TextValueObject.create(quoteDTO.notes); if (notesOrError.isFailure) { return Result.fail(notesOrError.error); } const validityOrError = Note.create(quoteDTO.validity); if (validityOrError.isFailure) { return Result.fail(validityOrError.error); } const discountOrError = Percentage.create(quoteDTO.discount); if (discountOrError.isFailure) { return Result.fail(discountOrError.error); } const taxOrError = Percentage.create(quoteDTO.tax); if (taxOrError.isFailure) { return Result.fail(taxOrError.error); } let items: Collection; try { items = new Collection( quoteDTO.items?.map((item) => { const articleIdOrError = ArticleIdentifier.create(item.article_id); if (articleIdOrError.isFailure) { throw articleIdOrError.error; } const descriptionOrError = Description.create(item.description); if (descriptionOrError.isFailure) { throw descriptionOrError.error; } const quantityOrError = Quantity.create({ amount: item.quantity.amount, scale: item.quantity.scale, }); if (quantityOrError.isFailure) { throw quantityOrError.error; } const unitPriceOrError = UnitPrice.create({ amount: item.unit_price?.amount, currencyCode: item.unit_price?.currency_code, scale: item.unit_price?.scale, }); if (unitPriceOrError.isFailure) { throw unitPriceOrError.error; } const percentageOrError = Percentage.create({ amount: item.discount?.amount, scale: item.discount?.scale, }); if (percentageOrError.isFailure) { throw percentageOrError.error; } const quoteItemOrError = QuoteItem.create({ articleId: articleIdOrError.object, description: descriptionOrError.object, quantity: quantityOrError.object, unitPrice: unitPriceOrError.object, discount: percentageOrError.object, }); if (quoteItemOrError.isFailure) { throw quoteItemOrError.error; } return quoteItemOrError.object; }) ); } catch (e: unknown) { return Result.fail(e as IDomainError); } return Quote.create( { status: statusOrError.object, date: dateOrError.object, reference: referenceOrError.object, language: languageOrError.object, customer: customerOrError.object, currency: currencyOrError.object, paymentMethod: paymentOrError.object, notes: notesOrError.object, validity: validityOrError.object, discount: discountOrError.object, tax: taxOrError.object, items, dealerId, }, quoteId ); } private _getQuoteRepository() { return this._repositoryManager.getRepository("Quote"); } }