This commit is contained in:
David Arranz 2024-07-18 12:02:59 +02:00
parent 47802ab932
commit 23cfe70578
10 changed files with 67 additions and 45 deletions

View File

@ -128,6 +128,7 @@ export const QuoteEdit = () => {
const onSubmit: SubmitHandler<QuoteDataForm> = async (data) => { const onSubmit: SubmitHandler<QuoteDataForm> = async (data) => {
// Transformación del form -> typo de request // Transformación del form -> typo de request
console.log(data);
mutate(data, { mutate(data, {
onError: (error) => { onError: (error) => {
console.debug(error); console.debug(error);
@ -163,19 +164,17 @@ export const QuoteEdit = () => {
if (name === "items") { if (name === "items") {
const { items } = value; const { items } = value;
let quoteSubtotal = MoneyValue.create().object;
let quoteSubtotal = MoneyValue.create({
amount: 0,
scale: 4,
}).object;
// Recálculo líneas // Recálculo líneas
items && items &&
items.map((item, index) => { items.map((item, index) => {
const itemTotals = calculateItemTotals(item); const itemTotals = calculateItemTotals(item);
if (itemTotals === null) {
return;
}
quoteSubtotal = quoteSubtotal.add(itemTotals.totalPrice); quoteSubtotal = quoteSubtotal.add(itemTotals.totalPrice);
setValue(`items.${index}.subtotal_price`, itemTotals.subtotalPrice.toObject()); setValue(`items.${index}.subtotal_price`, itemTotals.subtotalPrice.toObject());
setValue(`items.${index}.total_price`, itemTotals.totalPrice.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")) { if (name.endsWith("quantity") || name.endsWith("unit_price") || name.endsWith("discount")) {
const { items } = value; const { items } = value;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [, indexString, fieldName] = String(name).split("."); const [, indexString, fieldName] = String(name).split(".");
const index = parseInt(indexString); const index = parseInt(indexString);
const itemTotals = calculateItemTotals(items[index]); const itemTotals = calculateItemTotals(items[index]);
if (itemTotals === null) {
return;
}
setValue(`items.${index}.subtotal_price`, itemTotals.subtotalPrice.toObject()); setValue(`items.${index}.subtotal_price`, itemTotals.subtotalPrice.toObject());
setValue(`items.${index}.total_price`, itemTotals.totalPrice.toObject()); setValue(`items.${index}.total_price`, itemTotals.totalPrice.toObject());
@ -206,7 +201,7 @@ export const QuoteEdit = () => {
}, [watch, getValues, setValue]); }, [watch, getValues, setValue]);
if (isSubmitting) { if (isSubmitting) {
return <LoadingOverlay />; return <LoadingOverlay title='Guardando cotización' />;
} }
if (status === "error") { if (status === "error") {

View File

@ -1,13 +1,32 @@
import { MoneyValue, Percentage, Quantity } from "@shared/contexts"; import { MoneyValue, Percentage, Quantity } from "@shared/contexts";
export const calculateItemTotals = (item: any) => { export const calculateItemTotals = (item: any) => {
console.log(item);
const { quantity: quantity_dto, unit_price: unit_price_dto, discount: discount_dto } = item; const { quantity: quantity_dto, unit_price: unit_price_dto, discount: discount_dto } = item;
/*if (quantity_dto.amount === null || unit_price_dto.amount === null) { if (quantity_dto.amount === null || unit_price_dto.amount === null) {
return 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); const quantityOrError = Quantity.create(quantity_dto);
if (quantityOrError.isFailure) { if (quantityOrError.isFailure) {

View File

@ -16,6 +16,8 @@ export class ArticleIdentifier extends NullableValueObject<number> {
) { ) {
const ruleNull = RuleValidator.RULE_ALLOW_NULL_OR_UNDEFINED.default(null); 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( const ruleNumber = RuleValidator.RULE_IS_TYPE_NUMBER.label(
options.label ? options.label : "ArticleIdentifier" options.label ? options.label : "ArticleIdentifier"
); );
@ -24,11 +26,15 @@ export class ArticleIdentifier extends NullableValueObject<number> {
options.label ? options.label : "ArticleIdentifier" options.label ? options.label : "ArticleIdentifier"
); );
const rules = Joi.alternatives(ruleNull, ruleNumber, ruleString); const rules = Joi.alternatives(ruleNull, ruleEmpty, ruleNumber, ruleString);
return RuleValidator.validate<NullOr<number>>(rules, value); return RuleValidator.validate<NullOr<number>>(rules, value);
} }
private static sanitize(value: NullOr<number | string>): NullOr<number> {
return value ? Number(value) : null;
}
public static create(value: NullOr<number | string>, options: IArticleIdentifierOptions = {}) { public static create(value: NullOr<number | string>, options: IArticleIdentifierOptions = {}) {
const _options = { const _options = {
label: "ArticleIdentifier", label: "ArticleIdentifier",
@ -49,7 +55,7 @@ export class ArticleIdentifier extends NullableValueObject<number> {
_value = validationResult.object; _value = validationResult.object;
} }
return Result.ok<ArticleIdentifier>(new ArticleIdentifier(_value)); return Result.ok<ArticleIdentifier>(new ArticleIdentifier(ArticleIdentifier.sanitize(_value)));
} }
public toNumber(): number { public toNumber(): number {

View File

@ -117,7 +117,6 @@ export class UpdateQuoteUseCase
try { try {
await transaction.complete(async (t) => { await transaction.complete(async (t) => {
console.log(t);
quoteRepo = quoteRepository({ transaction: t }); quoteRepo = quoteRepository({ transaction: t });
await quoteRepo.update(quote); await quoteRepo.update(quote);
}); });

View File

@ -53,7 +53,9 @@ export class QuoteItem extends Entity<IQuoteItemProps> implements IQuoteItem {
} }
get subtotalPrice(): MoneyValue { 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 { get discount(): Percentage {
@ -61,6 +63,8 @@ export class QuoteItem extends Entity<IQuoteItemProps> implements IQuoteItem {
} }
get totalPrice(): MoneyValue { 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()));
} }
} }

View File

@ -43,7 +43,7 @@ const quoteItemPresenter = (
): IGetQuote_QuoteItem_Response_DTO[] => ): IGetQuote_QuoteItem_Response_DTO[] =>
items.totalCount > 0 items.totalCount > 0
? items.items.map((item: QuoteItem) => ({ ? items.items.map((item: QuoteItem) => ({
article_id: item.articleId ?? "", article_id: item.articleId.toString(),
description: item.description.toString(), description: item.description.toString(),
quantity: item.quantity.toObject(), quantity: item.quantity.toObject(),
unit_price: item.unitPrice.toObject(), unit_price: item.unitPrice.toObject(),

View File

@ -36,7 +36,7 @@ export const UpdateQuotePresenter: IUpdateQuotePresenter = {
const quoteItemPresenter = (items: ICollection<QuoteItem>, context: ISalesContext) => const quoteItemPresenter = (items: ICollection<QuoteItem>, context: ISalesContext) =>
items.totalCount > 0 items.totalCount > 0
? items.items.map((item: QuoteItem) => ({ ? items.items.map((item: QuoteItem) => ({
article_id: item.articleId ?? "", article_id: item.articleId.toString(),
description: item.description.toString(), description: item.description.toString(),
quantity: item.quantity.toObject(), quantity: item.quantity.toObject(),
unit_price: item.unitPrice.toObject(), unit_price: item.unitPrice.toObject(),

View File

@ -3,7 +3,7 @@ import DineroFactory, { Currency, Dinero } from "dinero.js";
import Joi from "joi"; import Joi from "joi";
import { isNull } from "lodash"; import { isNull } from "lodash";
import { NullOr, UndefinedOr } from "../../../../utilities"; import { NullOr } from "../../../../utilities";
import { DomainError, handleDomainError } from "../errors"; import { DomainError, handleDomainError } from "../errors";
import { RuleValidator } from "../RuleValidator"; import { RuleValidator } from "../RuleValidator";
import { CurrencyData } from "./CurrencyData"; import { CurrencyData } from "./CurrencyData";
@ -11,12 +11,10 @@ import { Result } from "./Result";
import { IValueObjectOptions, ValueObject } from "./ValueObject"; import { IValueObjectOptions, ValueObject } from "./ValueObject";
export interface IMoneyValueOptions extends IValueObjectOptions { export interface IMoneyValueOptions extends IValueObjectOptions {
defaultValue?: number;
locale: string; locale: string;
} }
export const defaultMoneyValueOptions: IMoneyValueOptions = { export const defaultMoneyValueOptions: IMoneyValueOptions = {
defaultValue: 0,
locale: "es-ES", locale: "es-ES",
}; };
@ -50,7 +48,7 @@ const defaultMoneyValueProps = {
}; };
interface IMoneyValue { interface IMoneyValue {
toPrimitive(): UndefinedOr<number>; toPrimitive(): NullOr<number>;
toPrimitives(): MoneyValueObject; toPrimitives(): MoneyValueObject;
isEmpty(): boolean; isEmpty(): boolean;
toString(): string; toString(): string;
@ -92,9 +90,6 @@ export class MoneyValue extends ValueObject<Dinero> implements IMoneyValue {
public static readonly DEFAULT_SCALE = defaultMoneyValueProps.scale; public static readonly DEFAULT_SCALE = defaultMoneyValueProps.scale;
public static readonly DEFAULT_CURRENCY_CODE = defaultMoneyValueProps.currencyCode; 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 _isNull: boolean;
private readonly _options: IMoneyValueOptions; private readonly _options: IMoneyValueOptions;
@ -103,12 +98,7 @@ export class MoneyValue extends ValueObject<Dinero> implements IMoneyValue {
.optional() // <- undefined .optional() // <- undefined
.valid(null); // <- null .valid(null); // <- null
const ruleNumber = Joi.number() const ruleNumber = Joi.number().label(options.label ? options.label : "amount");
.optional()
.default(0)
.min(-1000)
.max(this.MAX_VALUE)
.label(options.label ? options.label : "amount");
const rules = Joi.alternatives(ruleNull, ruleNumber); const rules = Joi.alternatives(ruleNull, ruleNumber);
@ -154,7 +144,7 @@ export class MoneyValue extends ValueObject<Dinero> implements IMoneyValue {
const _currency = CurrencyData.createFromCode(currencyCode).object.code; const _currency = CurrencyData.createFromCode(currencyCode).object.code;
const prop = DineroFactory({ const prop = DineroFactory({
amount: !isNull(_amount) ? Number(_amount) : options.defaultValue, amount: Number(_amount),
currency: _currency as Currency, currency: _currency as Currency,
precision: scale, precision: scale,
}).setLocale(options.locale); }).setLocale(options.locale);
@ -287,8 +277,8 @@ export class MoneyValue extends ValueObject<Dinero> implements IMoneyValue {
return this._isNull ? {} : this.props?.toJSON(); return this._isNull ? {} : this.props?.toJSON();
} }
public toPrimitive(): UndefinedOr<number> { public toPrimitive(): NullOr<number> {
return this._isNull ? undefined : Number(this.props?.getAmount()); return this._isNull ? null : Number(this.props?.getAmount());
} }
public toPrimitives(): MoneyValueObject { public toPrimitives(): MoneyValueObject {
@ -308,7 +298,16 @@ export class MoneyValue extends ValueObject<Dinero> implements IMoneyValue {
} }
public convertScale(newScale: number, roundingMode: RoundingMode = "HALF_UP"): MoneyValue { 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 { public getCurrency(): CurrencyData {

View File

@ -22,7 +22,7 @@ interface IPercentage {
} }
export interface PercentageObject { export interface PercentageObject {
amount: number; amount: number | null;
scale: number; scale: number;
} }
@ -259,7 +259,7 @@ export class Percentage extends NullableValueObject<IPercentage> {
public toObject(): PercentageObject { public toObject(): PercentageObject {
return { return {
amount: this.isNull() ? 0 : Number(this.amount), amount: this.amount,
scale: this.scale, scale: this.scale,
}; };
} }

View File

@ -195,7 +195,7 @@ export class Quantity extends NullableValueObject<IQuantity> {
} }
get scale(): number { get scale(): number {
return this.isNull() ? 0 : Number(this.props?.scale); return Number(this.props?.scale);
} }
public getAmount(): NullOr<number> { public getAmount(): NullOr<number> {