diff --git a/client/src/app/quotes/edit.tsx b/client/src/app/quotes/edit.tsx index 707c413..3cc3ee0 100644 --- a/client/src/app/quotes/edit.tsx +++ b/client/src/app/quotes/edit.tsx @@ -128,6 +128,7 @@ export const QuoteEdit = () => { const onSubmit: SubmitHandler = async (data) => { // Transformación del form -> typo de request + console.log(data); mutate(data, { onError: (error) => { console.debug(error); @@ -163,19 +164,17 @@ export const QuoteEdit = () => { if (name === "items") { const { items } = value; - let quoteSubtotal = MoneyValue.create().object; + + let quoteSubtotal = MoneyValue.create({ + amount: 0, + scale: 4, + }).object; // Recálculo líneas items && items.map((item, index) => { const itemTotals = calculateItemTotals(item); - - if (itemTotals === null) { - return; - } - quoteSubtotal = quoteSubtotal.add(itemTotals.totalPrice); - setValue(`items.${index}.subtotal_price`, itemTotals.subtotalPrice.toObject()); setValue(`items.${index}.total_price`, itemTotals.totalPrice.toObject()); }); @@ -186,15 +185,11 @@ export const QuoteEdit = () => { if (name.endsWith("quantity") || name.endsWith("unit_price") || name.endsWith("discount")) { const { items } = value; - // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [, indexString, fieldName] = String(name).split("."); const index = parseInt(indexString); const itemTotals = calculateItemTotals(items[index]); - if (itemTotals === null) { - return; - } - setValue(`items.${index}.subtotal_price`, itemTotals.subtotalPrice.toObject()); setValue(`items.${index}.total_price`, itemTotals.totalPrice.toObject()); @@ -206,7 +201,7 @@ export const QuoteEdit = () => { }, [watch, getValues, setValue]); if (isSubmitting) { - return ; + return ; } if (status === "error") { diff --git a/client/src/lib/calc.ts b/client/src/lib/calc.ts index 5b3e6c0..f70f844 100644 --- a/client/src/lib/calc.ts +++ b/client/src/lib/calc.ts @@ -1,13 +1,32 @@ import { MoneyValue, Percentage, Quantity } from "@shared/contexts"; export const calculateItemTotals = (item: any) => { - console.log(item); - const { quantity: quantity_dto, unit_price: unit_price_dto, discount: discount_dto } = item; - /*if (quantity_dto.amount === null || unit_price_dto.amount === null) { - return null; - }*/ + if (quantity_dto.amount === null || unit_price_dto.amount === null) { + return { + quantity: Quantity.create({ + amount: quantity_dto.amount, + scale: 0, + }).object, + unitPrice: MoneyValue.create({ + amount: unit_price_dto.amount, + scale: 4, + }).object, + subtotalPrice: MoneyValue.create({ + amount: null, + scale: 4, + }).object, + discount: Percentage.create({ + amount: discount_dto.amount, + scale: 2, + }).object, + totalPrice: MoneyValue.create({ + amount: null, + scale: 4, + }).object, + }; + } const quantityOrError = Quantity.create(quantity_dto); if (quantityOrError.isFailure) { diff --git a/server/src/contexts/catalog/domain/entities/ArticleIdentifier.ts b/server/src/contexts/catalog/domain/entities/ArticleIdentifier.ts index 88fe07b..c4087e7 100644 --- a/server/src/contexts/catalog/domain/entities/ArticleIdentifier.ts +++ b/server/src/contexts/catalog/domain/entities/ArticleIdentifier.ts @@ -16,6 +16,8 @@ export class ArticleIdentifier extends NullableValueObject { ) { const ruleNull = RuleValidator.RULE_ALLOW_NULL_OR_UNDEFINED.default(null); + const ruleEmpty = RuleValidator.RULE_ALLOW_EMPTY.default(null); + const ruleNumber = RuleValidator.RULE_IS_TYPE_NUMBER.label( options.label ? options.label : "ArticleIdentifier" ); @@ -24,11 +26,15 @@ export class ArticleIdentifier extends NullableValueObject { options.label ? options.label : "ArticleIdentifier" ); - const rules = Joi.alternatives(ruleNull, ruleNumber, ruleString); + const rules = Joi.alternatives(ruleNull, ruleEmpty, ruleNumber, ruleString); return RuleValidator.validate>(rules, value); } + private static sanitize(value: NullOr): NullOr { + return value ? Number(value) : null; + } + public static create(value: NullOr, options: IArticleIdentifierOptions = {}) { const _options = { label: "ArticleIdentifier", @@ -49,7 +55,7 @@ export class ArticleIdentifier extends NullableValueObject { _value = validationResult.object; } - return Result.ok(new ArticleIdentifier(_value)); + return Result.ok(new ArticleIdentifier(ArticleIdentifier.sanitize(_value))); } public toNumber(): number { diff --git a/server/src/contexts/sales/application/Quote/UpdateQuote.useCase.ts b/server/src/contexts/sales/application/Quote/UpdateQuote.useCase.ts index 76d956e..f66daa4 100644 --- a/server/src/contexts/sales/application/Quote/UpdateQuote.useCase.ts +++ b/server/src/contexts/sales/application/Quote/UpdateQuote.useCase.ts @@ -117,7 +117,6 @@ export class UpdateQuoteUseCase try { await transaction.complete(async (t) => { - console.log(t); quoteRepo = quoteRepository({ transaction: t }); await quoteRepo.update(quote); }); diff --git a/server/src/contexts/sales/domain/entities/Quotes/QuoteItem.ts b/server/src/contexts/sales/domain/entities/Quotes/QuoteItem.ts index 1bd79b8..613e1f5 100644 --- a/server/src/contexts/sales/domain/entities/Quotes/QuoteItem.ts +++ b/server/src/contexts/sales/domain/entities/Quotes/QuoteItem.ts @@ -53,7 +53,9 @@ export class QuoteItem extends Entity implements IQuoteItem { } get subtotalPrice(): MoneyValue { - return this.unitPrice.multiply(this.quantity.toNumber()); + return this.quantity.isNull() || this.unitPrice.isNull() + ? MoneyValue.create({ amount: null, scale: 4 }).object + : this.unitPrice.multiply(this.quantity.toNumber()); } get discount(): Percentage { @@ -61,6 +63,8 @@ export class QuoteItem extends Entity implements IQuoteItem { } get totalPrice(): MoneyValue { - return this.subtotalPrice.subtract(this.subtotalPrice.percentage(this.discount.toNumber())); + return this.subtotalPrice.isNull() + ? MoneyValue.create({ amount: null, scale: 4 }).object + : this.subtotalPrice.subtract(this.subtotalPrice.percentage(this.discount.toNumber())); } } diff --git a/server/src/contexts/sales/infrastructure/express/controllers/quotes/getQuote/presenter/GetQuote.presenter.ts b/server/src/contexts/sales/infrastructure/express/controllers/quotes/getQuote/presenter/GetQuote.presenter.ts index c672612..5afe120 100644 --- a/server/src/contexts/sales/infrastructure/express/controllers/quotes/getQuote/presenter/GetQuote.presenter.ts +++ b/server/src/contexts/sales/infrastructure/express/controllers/quotes/getQuote/presenter/GetQuote.presenter.ts @@ -43,7 +43,7 @@ const quoteItemPresenter = ( ): IGetQuote_QuoteItem_Response_DTO[] => items.totalCount > 0 ? items.items.map((item: QuoteItem) => ({ - article_id: item.articleId ?? "", + article_id: item.articleId.toString(), description: item.description.toString(), quantity: item.quantity.toObject(), unit_price: item.unitPrice.toObject(), diff --git a/server/src/contexts/sales/infrastructure/express/controllers/quotes/updateQuote/presenter/UpdateQuote.presenter.ts b/server/src/contexts/sales/infrastructure/express/controllers/quotes/updateQuote/presenter/UpdateQuote.presenter.ts index a3e03a6..5c97ff5 100644 --- a/server/src/contexts/sales/infrastructure/express/controllers/quotes/updateQuote/presenter/UpdateQuote.presenter.ts +++ b/server/src/contexts/sales/infrastructure/express/controllers/quotes/updateQuote/presenter/UpdateQuote.presenter.ts @@ -36,7 +36,7 @@ export const UpdateQuotePresenter: IUpdateQuotePresenter = { const quoteItemPresenter = (items: ICollection, context: ISalesContext) => items.totalCount > 0 ? items.items.map((item: QuoteItem) => ({ - article_id: item.articleId ?? "", + article_id: item.articleId.toString(), description: item.description.toString(), quantity: item.quantity.toObject(), unit_price: item.unitPrice.toObject(), diff --git a/shared/lib/contexts/common/domain/entities/MoneyValue.ts b/shared/lib/contexts/common/domain/entities/MoneyValue.ts index d032e2a..87e9c2c 100644 --- a/shared/lib/contexts/common/domain/entities/MoneyValue.ts +++ b/shared/lib/contexts/common/domain/entities/MoneyValue.ts @@ -3,7 +3,7 @@ import DineroFactory, { Currency, Dinero } from "dinero.js"; import Joi from "joi"; import { isNull } from "lodash"; -import { NullOr, UndefinedOr } from "../../../../utilities"; +import { NullOr } from "../../../../utilities"; import { DomainError, handleDomainError } from "../errors"; import { RuleValidator } from "../RuleValidator"; import { CurrencyData } from "./CurrencyData"; @@ -11,12 +11,10 @@ import { Result } from "./Result"; import { IValueObjectOptions, ValueObject } from "./ValueObject"; export interface IMoneyValueOptions extends IValueObjectOptions { - defaultValue?: number; locale: string; } export const defaultMoneyValueOptions: IMoneyValueOptions = { - defaultValue: 0, locale: "es-ES", }; @@ -50,7 +48,7 @@ const defaultMoneyValueProps = { }; interface IMoneyValue { - toPrimitive(): UndefinedOr; + toPrimitive(): NullOr; toPrimitives(): MoneyValueObject; isEmpty(): boolean; toString(): string; @@ -92,9 +90,6 @@ export class MoneyValue extends ValueObject implements IMoneyValue { public static readonly DEFAULT_SCALE = defaultMoneyValueProps.scale; public static readonly DEFAULT_CURRENCY_CODE = defaultMoneyValueProps.currencyCode; - private static readonly MIN_VALUE = Number.MIN_VALUE; - private static readonly MAX_VALUE = Number.MAX_VALUE; - private readonly _isNull: boolean; private readonly _options: IMoneyValueOptions; @@ -103,12 +98,7 @@ export class MoneyValue extends ValueObject implements IMoneyValue { .optional() // <- undefined .valid(null); // <- null - const ruleNumber = Joi.number() - .optional() - .default(0) - .min(-1000) - .max(this.MAX_VALUE) - .label(options.label ? options.label : "amount"); + const ruleNumber = Joi.number().label(options.label ? options.label : "amount"); const rules = Joi.alternatives(ruleNull, ruleNumber); @@ -154,7 +144,7 @@ export class MoneyValue extends ValueObject implements IMoneyValue { const _currency = CurrencyData.createFromCode(currencyCode).object.code; const prop = DineroFactory({ - amount: !isNull(_amount) ? Number(_amount) : options.defaultValue, + amount: Number(_amount), currency: _currency as Currency, precision: scale, }).setLocale(options.locale); @@ -287,8 +277,8 @@ export class MoneyValue extends ValueObject implements IMoneyValue { return this._isNull ? {} : this.props?.toJSON(); } - public toPrimitive(): UndefinedOr { - return this._isNull ? undefined : Number(this.props?.getAmount()); + public toPrimitive(): NullOr { + return this._isNull ? null : Number(this.props?.getAmount()); } public toPrimitives(): MoneyValueObject { @@ -308,7 +298,16 @@ export class MoneyValue extends ValueObject implements IMoneyValue { } public convertScale(newScale: number, roundingMode: RoundingMode = "HALF_UP"): MoneyValue { - return MoneyValue.createFromDinero(this.props.convertPrecision(newScale, roundingMode)).object; + if (this._isNull) { + return MoneyValue.create({ + amount: null, + scale: newScale, + currencyCode: this.getCurrency().code, + }).object; + } else { + return MoneyValue.createFromDinero(this.props.convertPrecision(newScale, roundingMode)) + .object; + } } public getCurrency(): CurrencyData { diff --git a/shared/lib/contexts/common/domain/entities/Percentage.ts b/shared/lib/contexts/common/domain/entities/Percentage.ts index e5500bd..7505e62 100644 --- a/shared/lib/contexts/common/domain/entities/Percentage.ts +++ b/shared/lib/contexts/common/domain/entities/Percentage.ts @@ -22,7 +22,7 @@ interface IPercentage { } export interface PercentageObject { - amount: number; + amount: number | null; scale: number; } @@ -259,7 +259,7 @@ export class Percentage extends NullableValueObject { public toObject(): PercentageObject { return { - amount: this.isNull() ? 0 : Number(this.amount), + amount: this.amount, scale: this.scale, }; } diff --git a/shared/lib/contexts/common/domain/entities/Quantity.ts b/shared/lib/contexts/common/domain/entities/Quantity.ts index ed52e64..0f87ac4 100644 --- a/shared/lib/contexts/common/domain/entities/Quantity.ts +++ b/shared/lib/contexts/common/domain/entities/Quantity.ts @@ -195,7 +195,7 @@ export class Quantity extends NullableValueObject { } get scale(): number { - return this.isNull() ? 0 : Number(this.props?.scale); + return Number(this.props?.scale); } public getAmount(): NullOr {