Presupuestador_web/server/src/contexts/sales/application/Quote/CreateQuote.useCase.ts

303 lines
9.1 KiB
TypeScript
Raw Normal View History

2024-07-19 15:02:19 +00:00
import { ArticleIdentifier } from "@/contexts/catalog/domain";
2024-05-26 17:09:43 +00:00
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";
2024-08-23 16:47:51 +00:00
import { SequelizeBusinessTransactionType } from "@/contexts/common/infrastructure/sequelize/SequelizeBusinessTransaction";
2024-05-26 17:09:43 +00:00
import {
Collection,
2024-07-09 16:21:12 +00:00
CurrencyData,
2024-05-26 17:09:43 +00:00
Description,
DomainError,
ICreateQuote_Request_DTO,
IDomainError,
Language,
2024-07-09 16:21:12 +00:00
Percentage,
2024-05-26 17:09:43 +00:00
Quantity,
Result,
2024-07-12 16:37:41 +00:00
TextValueObject,
2024-05-26 17:09:43 +00:00
UTCDateValue,
UniqueID,
UnitPrice,
ensureIdIsValid,
} from "@shared/contexts";
2024-07-03 10:27:58 +00:00
import {
Dealer,
IQuoteRepository,
Quote,
QuoteCustomer,
2024-08-23 16:47:51 +00:00
QuoteCustomerReference,
2024-07-03 10:27:58 +00:00
QuoteItem,
QuoteReference,
QuoteStatus,
} from "../../domain";
import { ISalesContext } from "../../infrastructure";
2024-08-23 16:47:51 +00:00
import { generateQuoteReferenceForDealer } from "../services";
2024-05-26 17:09:43 +00:00
export type CreateQuoteResponseOrError =
| Result<never, IUseCaseError> // Misc errors (value objects)
| Result<Quote, never>; // Success!
export class CreateQuoteUseCase
implements IUseCase<ICreateQuote_Request_DTO, Promise<CreateQuoteResponseOrError>>
{
private _adapter: ISequelizeAdapter;
private _repositoryManager: IRepositoryManager;
2024-08-23 16:47:51 +00:00
private _dealer: Dealer;
private _transaction: SequelizeBusinessTransactionType;
2024-05-26 17:09:43 +00:00
2024-07-03 10:27:58 +00:00
constructor(context: ISalesContext) {
this._adapter = context.adapter;
this._repositoryManager = context.repositoryManager;
2024-08-23 16:47:51 +00:00
this._dealer = context.dealer!;
2024-05-26 17:09:43 +00:00
}
async execute(request: ICreateQuote_Request_DTO) {
const { id } = request;
// Validaciones de datos
2024-07-03 10:27:58 +00:00
if (!this._dealer) {
const message = "Error. Missing Dealer";
return Result.fail(UseCaseError.create(UseCaseError.INVALID_INPUT_DATA, message));
}
2024-05-26 17:09:43 +00:00
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" }])
);
}
2024-08-23 16:47:51 +00:00
this._transaction = this._adapter.startTransaction();
2024-05-26 17:09:43 +00:00
const quoteRepository = this._getQuoteRepository();
2024-08-23 16:47:51 +00:00
try {
return await this._transaction.complete(async (t) => {
const quoteRepo = quoteRepository({ transaction: t });
// Comprobar que no existe un quote previo con esos datos
const idExists = await quoteRepo.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",
})
);
}
// Generate Reference
const quoteReference = await generateQuoteReferenceForDealer(this._dealer, quoteRepo);
// Crear quote
const quoteOrError = this._tryCreateQuoteInstance(
request,
idOrError.object,
quoteReference
);
if (quoteOrError.isFailure) {
const { error: domainError } = quoteOrError;
let errorCode = "";
let message = "";
switch (domainError.code) {
case DomainError.INVALID_INPUT_DATA:
errorCode = UseCaseError.INVALID_INPUT_DATA;
2024-09-16 17:31:41 +00:00
message = "La cotización tiene algún dato erróneo.";
2024-08-23 16:47:51 +00:00
break;
default:
errorCode = UseCaseError.UNEXCEPTED_ERROR;
message = domainError.message;
break;
}
2024-05-26 17:09:43 +00:00
2024-08-23 16:47:51 +00:00
return Result.fail(UseCaseError.create(errorCode, message, domainError));
}
2024-05-26 17:09:43 +00:00
2024-08-23 16:47:51 +00:00
await quoteRepo.create(quoteOrError.object);
2024-05-26 17:09:43 +00:00
2024-08-23 16:47:51 +00:00
return Result.ok<Quote>(quoteOrError.object);
2024-05-26 17:09:43 +00:00
});
} catch (error: unknown) {
const _error = error as IInfrastructureError;
return Result.fail(UseCaseError.create(UseCaseError.REPOSITORY_ERROR, _error.message));
}
}
private _tryCreateQuoteInstance(
quoteDTO: ICreateQuote_Request_DTO,
2024-07-03 10:27:58 +00:00
quoteId: UniqueID,
2024-08-23 16:47:51 +00:00
quoteReference: QuoteReference
2024-05-26 17:09:43 +00:00
): Result<Quote, IDomainError> {
2024-08-23 16:47:51 +00:00
const dealerId: UniqueID = this._dealer.id;
2024-05-26 17:09:43 +00:00
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);
}
2024-07-09 16:21:21 +00:00
const languageOrError = Language.createFromCode(
quoteDTO.lang_code ?? this._dealer?.language.code
);
2024-05-26 17:09:43 +00:00
if (languageOrError.isFailure) {
return Result.fail(languageOrError.error);
}
2024-08-23 16:47:51 +00:00
const customerReferenceOrError = QuoteCustomerReference.create(quoteDTO.customer_reference);
if (customerReferenceOrError.isFailure) {
return Result.fail(customerReferenceOrError.error);
}
2024-07-01 17:12:15 +00:00
const customerOrError = QuoteCustomer.create(quoteDTO.customer_information);
if (customerOrError.isFailure) {
return Result.fail(customerOrError.error);
}
2024-07-09 16:21:21 +00:00
const currencyOrError = CurrencyData.createFromCode(
quoteDTO.currency_code ?? CurrencyData.DEFAULT_CURRENCY_CODE
);
2024-05-26 17:09:43 +00:00
if (currencyOrError.isFailure) {
return Result.fail(currencyOrError.error);
}
const paymentOrError = TextValueObject.create(
quoteDTO.payment_method ??
this._dealer?.additionalInfo.get("default_payment_method")?.toString()
);
2024-07-01 17:12:15 +00:00
if (paymentOrError.isFailure) {
return Result.fail(paymentOrError.error);
}
const notesOrError = TextValueObject.create(
quoteDTO.notes ?? this._dealer?.additionalInfo.get("default_notes")?.toString()
);
2024-07-01 17:12:15 +00:00
if (notesOrError.isFailure) {
return Result.fail(notesOrError.error);
}
const validityOrError = TextValueObject.create(
quoteDTO.validity ?? this._dealer?.additionalInfo.get("default_quote_validity")?.toString()
);
2024-07-01 17:12:15 +00:00
if (validityOrError.isFailure) {
return Result.fail(validityOrError.error);
}
2024-07-09 16:21:21 +00:00
const discountOrError = Percentage.create(quoteDTO.discount);
if (discountOrError.isFailure) {
return Result.fail(discountOrError.error);
}
2024-09-16 17:31:41 +00:00
const taxOrError = Percentage.create(
quoteDTO.tax ?? {
amount: this._dealer?.additionalInfo.get("default_tax")?.toString(),
scale: 2,
}
);
2024-07-19 15:02:19 +00:00
if (taxOrError.isFailure) {
return Result.fail(taxOrError.error);
}
2024-10-03 14:23:46 +00:00
const dateSentOrError = UTCDateValue.create(null);
if (dateSentOrError.isFailure) {
return Result.fail(dateSentOrError.error);
}
// Items
2024-07-19 15:02:19 +00:00
let items: Collection<QuoteItem>;
try {
items = new Collection<QuoteItem>(
quoteDTO.items?.map((item) => {
2024-09-23 17:37:09 +00:00
const idArticleOrError = ArticleIdentifier.create(item.id_article);
if (idArticleOrError.isFailure) {
throw idArticleOrError.error;
2024-07-19 15:02:19 +00:00
}
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({
2024-09-23 17:37:09 +00:00
idArticle: idArticleOrError.object,
2024-07-19 15:02:19 +00:00
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);
}
2024-05-26 17:09:43 +00:00
return Quote.create(
{
status: statusOrError.object,
date: dateOrError.object,
2024-08-23 16:47:51 +00:00
reference: quoteReference,
2024-05-26 17:09:43 +00:00
language: languageOrError.object,
2024-08-23 16:47:51 +00:00
customerReference: customerReferenceOrError.object,
2024-07-01 17:12:15 +00:00
customer: customerOrError.object,
2024-05-26 17:09:43 +00:00
currency: currencyOrError.object,
2024-07-01 17:12:15 +00:00
paymentMethod: paymentOrError.object,
notes: notesOrError.object,
validity: validityOrError.object,
2024-07-09 16:21:21 +00:00
discount: discountOrError.object,
2024-07-19 15:02:19 +00:00
tax: taxOrError.object,
2024-07-09 16:21:21 +00:00
2024-05-26 17:09:43 +00:00
items,
2024-07-03 10:27:58 +00:00
dealerId,
2024-10-03 14:23:46 +00:00
dateSent: dateSentOrError.object,
2024-05-26 17:09:43 +00:00
},
quoteId
);
}
private _getQuoteRepository() {
return this._repositoryManager.getRepository<IQuoteRepository>("Quote");
}
}