This commit is contained in:
David Arranz 2024-07-15 17:41:42 +02:00
parent ab36d6e084
commit b9efe95289
31 changed files with 112 additions and 95 deletions

View File

@ -20,10 +20,10 @@ class ArticleMapper
const id_article = this.mapsValue(source, "id_article", ArticleIdentifier.create);
const reference = this.mapsValue(source, "reference", Slug.create);
const points = this.mapsValue(source, "points", (value: any) =>
Quantity.create({ amount: value, precision: 4 })
Quantity.create({ amount: value, scale: 4 })
);
const retail_price = this.mapsValue(source, "retail_price", (value: any) =>
UnitPrice.create({ amount: value, precision: 4 })
UnitPrice.create({ amount: value, scale: 4 })
);
const language = this.mapsValue(source, "lang_code", Language.createFromCode);

View File

@ -1,9 +1,6 @@
// https://github.com/Hodor9898/sequelize-query-builder/blob/master/index.ts
import { IOrder, IOrderCollection } from "@shared/contexts";
export class SequelizeParseOrder {
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
static parseOrder(orderCollection: IOrderCollection): any {
if (orderCollection.totalCount === 0) {
return null;
@ -24,3 +21,7 @@ export class SequelizeParseOrder {
});
}
}
/*
// https://github.com/Hodor9898/sequelize-query-builder/blob/master/index.ts
*/

View File

@ -10,11 +10,11 @@ export const UpdateProfilePresenter: IUpdateProfilePresenter = {
map: (profile: Profile, context: IProfileContext): IUpdateProfileResponse_DTO => {
return {
dealer_id: profile.id.toString(),
contact_information: profile.contactInformation,
default_payment_method: profile.defaultPaymentMethod,
default_notes: profile.defaultNotes,
default_legal_terms: profile.defaultLegalTerms,
default_quote_validity: profile.defaultQuoteValidity,
contact_information: profile.contactInformation.toString(),
default_payment_method: profile.defaultPaymentMethod.toString(),
default_notes: profile.defaultNotes.toString(),
default_legal_terms: profile.defaultLegalTerms.toString(),
default_quote_validity: profile.defaultQuoteValidity.toString(),
};
},
};

View File

@ -3,16 +3,18 @@ import { IRepositoryManager } from "@/contexts/common/domain";
import { IInfrastructureError } from "@/contexts/common/infrastructure";
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
import {
CurrencyData,
DomainError,
ICreateDealer_Request_DTO,
IDomainError,
Language,
Name,
Result,
UniqueID,
ensureIdIsValid,
ensureNameIsValid,
} from "@shared/contexts";
import { Dealer, IDealerRepository } from "../../domain";
import { Dealer, DealerStatus, IDealerRepository } from "../../domain";
export type CreateDealerResponseOrError =
| Result<never, IUseCaseError> // Misc errors (value objects)
@ -119,6 +121,10 @@ export class CreateDealerUseCase
return Dealer.create(
{
name: nameOrError.object,
currency: CurrencyData.createDefaultCode().object,
language: Language.createDefaultCode().object,
status: DealerStatus.createActive(),
//user_id: user
},
dealerId
);

View File

@ -194,11 +194,11 @@ export class CreateQuoteUseCase
unitPrice: UnitPrice.create({
amount: item.unit_price?.amount,
currencyCode: item.unit_price?.currency_code,
precision: item.unit_price?.precision,
scale: item.unit_price?.scale,
}).object,
discount: Percentage.create({
amount: item.discount?.amount,
precision: item.discount?.precision,
scale: item.discount?.scale,
}).object,
}).object
)

View File

@ -193,11 +193,11 @@ export class UpdateQuoteUseCase
unitPrice: UnitPrice.create({
amount: item.unit_price?.amount,
currencyCode: item.unit_price?.currency_code,
precision: item.unit_price?.precision,
scale: item.unit_price?.scale,
}).object,
discount: Percentage.create({
amount: item.discount?.amount,
precision: item.discount?.precision,
scale: item.discount?.scale,
}).object,
}).object
)

View File

@ -76,7 +76,7 @@ export class Quote extends AggregateRoot<IQuoteProps> implements IQuote {
.toArray()
.reduce<MoneyValue>(
(accumulator, currentItem) => accumulator.add(currentItem.subtotalPrice),
MoneyValue.create({ amount: 0, precision: 2, currencyCode: this.currency.code }).object
MoneyValue.create({ amount: 0, scale: 2, currencyCode: this.currency.code }).object
);
protected constructor(props: IQuoteProps, id?: UniqueID) {

View File

@ -11,7 +11,7 @@ import {
} from "@shared/contexts";
export interface IQuoteItemProps extends IEntityProps {
articleId: string;
articleId: string | null;
description: Description; // Descripción del artículo o servicio
quantity: Quantity; // Cantidad de unidades
unitPrice: MoneyValue; // Precio unitario en la moneda de la factura
@ -21,7 +21,7 @@ export interface IQuoteItemProps extends IEntityProps {
}
export interface IQuoteItem {
articleId: string;
articleId: string | null;
description: Description;
quantity: Quantity;
unitPrice: MoneyValue;
@ -35,7 +35,7 @@ export class QuoteItem extends Entity<IQuoteItemProps> implements IQuoteItem {
return Result.ok(new QuoteItem(props, id));
}
get articleId(): string {
get articleId(): string | null {
return this.props.articleId;
}

View File

@ -12,6 +12,7 @@ export const GetDealerPresenter: IGetDealerPresenter = {
return {
id: dealer.id.toString(),
name: dealer.name.toString(),
lang_code: dealer.language.code,
};
},
};

View File

@ -21,6 +21,8 @@ export const listDealersPresenter: IListDealersPresenter = {
return {
id: dealer.id.toString(),
name: dealer.name.toString(),
lang_code: dealer.language.code,
status: dealer.status.toString(),
};
},

View File

@ -41,17 +41,17 @@ const quoteItemPresenter = (items: ICollection<QuoteItem>, context: ISalesContex
unit_measure: "",
unit_price: {
amount: 0,
precision: 2,
scale: 2,
currency: "EUR",
},
subtotal: {
amount: 0,
precision: 2,
scale: 2,
currency: "EUR",
},
total: {
amount: 0,
precision: 2,
scale: 2,
currency: "EUR",
},
}))

View File

@ -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 ?? "",
description: item.description.toString(),
quantity: item.quantity.toObject(),
unit_price: item.unitPrice.toObject(),

View File

@ -64,7 +64,7 @@ class QuoteMapper
discount: this.mapsValue(source, "discount", (discount) =>
Percentage.create({
amount: discount,
precision: Percentage.DEFAULT_PRECISION,
scale: Percentage.DEFAULT_SCALE,
})
),

View File

@ -28,14 +28,14 @@ class QuoteItemMapper
quantity: this.mapsValue(source, "quantity", (quantity) =>
Quantity.create({
amount: quantity,
precision: Quantity.DEFAULT_PRECISION,
scale: Quantity.DEFAULT_SCALE,
})
),
unitPrice: this.mapsValue(source, "unit_price", (unit_price) =>
MoneyValue.create({
amount: unit_price,
currencyCode: sourceParent.currency_code,
precision: 4,
scale: 4,
})
),
@ -43,7 +43,7 @@ class QuoteItemMapper
MoneyValue.create({
amount: subtotal_price,
currencyCode: sourceParent.currency_code,
precision: 4,
scale: 4,
})
),
*/
@ -51,7 +51,7 @@ class QuoteItemMapper
discount: this.mapsValue(source, "discount", (discount) =>
Percentage.create({
amount: discount,
precision: Percentage.DEFAULT_PRECISION,
scale: Percentage.DEFAULT_SCALE,
})
),
@ -59,7 +59,7 @@ class QuoteItemMapper
MoneyValue.create({
amount: total_price,
currencyCode: sourceParent.currency_code,
precision: 2,
scale: 2,
})
),*/
};

View File

@ -46,7 +46,7 @@ export class Dealer_Model extends Model<
}
declare id: string;
declare contact_id?: CreationOptional<string>;
declare contact_id?: CreationOptional<string | null>;
declare name: CreationOptional<string>;
declare contact_information: CreationOptional<string>;
declare default_payment_method: CreationOptional<string>;

View File

@ -30,14 +30,14 @@ export class QuoteItem_Model extends Model<
declare quote_id: string;
declare item_id: string;
declare id_article: string; // number ??
declare id_article: CreationOptional<string | null>;
declare position: number;
declare description: CreationOptional<string>;
declare quantity: CreationOptional<number>;
declare unit_price: CreationOptional<number>;
declare subtotal_price: CreationOptional<number>;
declare discount: CreationOptional<number>;
declare total_price: CreationOptional<number>;
declare description: CreationOptional<string | null>;
declare quantity: CreationOptional<number | null>;
declare unit_price: CreationOptional<number | null>;
declare subtotal_price: CreationOptional<number | null>;
declare discount: CreationOptional<number | null>;
declare total_price: CreationOptional<number | null>;
declare quote: NonAttribute<Quote_Model>;
}

View File

@ -7,6 +7,7 @@ import {
Email,
ICreateUser_Request_DTO,
IDomainError,
Language,
Name,
Result,
UniqueID,
@ -168,6 +169,7 @@ export class CreateUserUseCase
email: emailOrError.object,
password: passwordOrError.object,
roles: [UserRole.ROLE_USER],
language: Language.createDefaultCode().object,
},
userId
);

View File

@ -12,6 +12,7 @@ import {
Email,
IDomainError,
IUpdateUser_Request_DTO,
Language,
Name,
Result,
UniqueID,
@ -129,6 +130,7 @@ export class UpdateUserUseCase
email: emailOrError.object,
password: passwordOrError.object,
roles: [UserRole.ROLE_USER],
language: Language.createDefaultCode().object,
},
userId
);

View File

@ -40,7 +40,7 @@ class UserMapper
name: source.name.toPrimitive(),
email: source.email.toPrimitive(),
password: source.password.toPrimitive(),
language: source.language.toPrimitive(),
lang_code: source.language.toPrimitive(),
roles: source.getRoles().map((rol) => rol.toPrimitive()),
};
}

View File

@ -1,4 +1,4 @@
import { IQuantuty_Response_DTO } from "../../../../common";
import { IQuantity_Response_DTO } from "../../../../common";
export interface IListArticles_Response_DTO {
id: string;
@ -10,5 +10,5 @@ export interface IListArticles_Response_DTO {
description: string;
points: number;
retail_price: IQuantuty_Response_DTO;
retail_price: IQuantity_Response_DTO;
}

View File

@ -69,11 +69,11 @@ export const ensureDescriptionIsValid = (value: string): Result<boolean, Error>
};*/
export const ensureUnitPriceIsValid = (value: any): Result<boolean, Error> => {
const { amount, currency, precision } = value;
const { amount, currency, scale } = value;
const descriptionOrError = UnitPrice.create({
amount,
currencyCode: currency,
precision,
scale,
});
return descriptionOrError.isSuccess ? Result.ok(true) : Result.fail(descriptionOrError.error);

View File

@ -1,16 +1,19 @@
import Joi from "joi";
import { Result, RuleValidator } from "../../domain";
export interface IMoney_Request_DTO {
export interface IMoney_DTO {
amount: number;
precision: number;
scale: number;
currency_code: string;
}
export interface IMoney_Request_DTO extends IMoney_DTO {}
export interface IMoney_Response_DTO extends IMoney_DTO {}
export function ensureMoney_DTOIsValid(money: IMoney_Request_DTO) {
const schema = Joi.object({
amount: Joi.number(),
precision: Joi.number(),
scale: Joi.number(),
currency_code: Joi.string(),
});
@ -22,5 +25,3 @@ export function ensureMoney_DTOIsValid(money: IMoney_Request_DTO) {
return Result.ok(true);
}
export interface IMoney_Response_DTO extends IMoney_Request_DTO {}

View File

@ -1,6 +1,6 @@
export interface IPercentage_DTO {
amount: number;
precision: number;
scale: number;
}
export interface IPercentage_Request_DTO extends IPercentage_DTO {}

View File

@ -3,13 +3,13 @@ import { Result, RuleValidator } from "../../domain";
export interface IQuantity_Request_DTO {
amount: number;
precision: number;
scale: number;
}
export function ensureQuantity_DTOIsValid(quantity: IQuantity_Request_DTO) {
const schema = Joi.object({
amount: Joi.number(),
precision: Joi.number(),
scale: Joi.number(),
});
const result = RuleValidator.validate<IQuantity_Request_DTO>(schema, quantity);

View File

@ -14,7 +14,7 @@ describe("MoneyValue Value Object", () => {
// Prueba la creación de un valor monetario nulo.
it("Should create a valid null money value", () => {
const nullMoneyValue = MoneyValue.create({
amount: null
amount: null,
});
expect(nullMoneyValue.isSuccess).toBe(true);
@ -43,8 +43,8 @@ describe("MoneyValue Value Object", () => {
it("should create MoneyValue from number and currency", () => {
const result = MoneyValue.create({
amount: 100,
precision: 3,
currencyCode: 'EUR'
scale: 3,
currencyCode: "EUR",
});
expect(result.isSuccess).toBe(true);
@ -58,8 +58,8 @@ describe("MoneyValue Value Object", () => {
it("should create MoneyValue from string and currency", () => {
const result = MoneyValue.create({
amount: "12345",
precision: 2,
currencyCode: 'USD'
scale: 2,
currencyCode: "USD",
});
expect(result.isSuccess).toBe(true);
@ -73,8 +73,8 @@ describe("MoneyValue Value Object", () => {
it("should fail to create MoneyValue with invalid amount", () => {
const result = MoneyValue.create({
amount: "invalid",
precision: 2,
currencyCode: 'USD'
scale: 2,
currencyCode: "USD",
});
expect(result.isFailure).toBe(true);
});
@ -96,12 +96,12 @@ describe("MoneyValue Value Object", () => {
// Prueba la verificación de valor cero.
it("Should check if value is zero", () => {
const zeroMoneyValue = MoneyValue.create(({
const zeroMoneyValue = MoneyValue.create({
amount: 0,
})).object;
const nonZeroMoneyValue = MoneyValue.create(({
}).object;
const nonZeroMoneyValue = MoneyValue.create({
amount: 50,
})).object;
}).object;
expect(zeroMoneyValue.isZero()).toBe(true);
expect(nonZeroMoneyValue.isZero()).toBe(false);

View File

@ -21,7 +21,7 @@ export const defaultMoneyValueOptions: IMoneyValueOptions = {
export interface MoneyValueObject {
amount: number;
precision: number;
scale: number;
currency_code: string;
}
@ -39,13 +39,13 @@ export { RoundingMode };
export interface IMoneyValueProps {
amount: NullOr<number | string>;
currencyCode?: string;
precision?: number;
scale?: number;
}
const defaultMoneyValueProps = {
amount: 0,
currencyCode: CurrencyData.DEFAULT_CURRENCY_CODE,
precision: 2,
scale: 2,
};
interface IMoneyValue {
@ -59,8 +59,8 @@ interface IMoneyValue {
getAmount(): number;
getCurrency(): CurrencyData;
getLocale(): string;
getPrecision(): number;
convertPrecision(newPrecision: number, roundingMode?: RoundingMode): MoneyValue;
getScale(): number;
convertScale(newScale: number, roundingMode?: RoundingMode): MoneyValue;
add(addend: MoneyValue): MoneyValue;
subtract(subtrahend: MoneyValue): MoneyValue;
@ -88,7 +88,7 @@ interface IMoneyValue {
}
export class MoneyValue extends ValueObject<Dinero> implements IMoneyValue {
public static readonly DEFAULT_PRECISION = defaultMoneyValueProps.precision;
public static readonly DEFAULT_SCALE = defaultMoneyValueProps.scale;
public static readonly DEFAULT_CURRENCY_CODE = defaultMoneyValueProps.currencyCode;
private static readonly MIN_VALUE = Number.MIN_VALUE;
@ -116,15 +116,15 @@ export class MoneyValue extends ValueObject<Dinero> implements IMoneyValue {
protected static getMonetaryValueInfo(amount: string): [string, number] {
// Divide la cadena de entrada en dos partes: valor y precisión
const [valuePart, precisionPart] = amount.split(".");
const [valuePart, scalePart] = amount.split(".");
// Calcula la precisión utilizada
const precision = precisionPart ? precisionPart.length : 0;
const scale = scalePart ? scalePart.length : 0;
// Elimina cualquier carácter no numérico de la parte del valor y concaténalo
const sanitizedValue = (valuePart + precisionPart).replace(/[^0-9]/g, "");
const sanitizedValue = (valuePart + scalePart).replace(/[^0-9]/g, "");
return [sanitizedValue, precision];
return [sanitizedValue, scale];
}
public static create(
@ -138,7 +138,7 @@ export class MoneyValue extends ValueObject<Dinero> implements IMoneyValue {
const {
amount = defaultMoneyValueProps.amount,
currencyCode = defaultMoneyValueProps.currencyCode,
precision = defaultMoneyValueProps.precision,
scale = defaultMoneyValueProps.scale,
} = props || {};
const validationResult = MoneyValue.validate(amount, options);
@ -153,7 +153,7 @@ export class MoneyValue extends ValueObject<Dinero> implements IMoneyValue {
const prop = DineroFactory({
amount: !isNull(_amount) ? Number(_amount) : options.defaultValue,
currency: _currency as Currency,
precision,
precision: scale,
}).setLocale(options.locale);
return Result.ok<MoneyValue>(new this(prop, isNull(_amount), options));
@ -175,7 +175,7 @@ export class MoneyValue extends ValueObject<Dinero> implements IMoneyValue {
return Result.ok<MoneyValue>(new MoneyValue(dinero, false, defaultMoneyValueOptions));
}
public static normalizePrecision(objects: ReadonlyArray<MoneyValue>): MoneyValue[] {
public static normalizeScale(objects: ReadonlyArray<MoneyValue>): MoneyValue[] {
return DineroFactory.normalizePrecision(objects.map((object) => object.props)).map(
(dinero) => MoneyValue.createFromDinero(dinero).object
);
@ -225,13 +225,12 @@ export class MoneyValue extends ValueObject<Dinero> implements IMoneyValue {
return this.props.getAmount();
}
public getPrecision(): number {
public getScale(): number {
return this.props.getPrecision();
}
public convertPrecision(newPrecision: number, roundingMode?: RoundingMode): MoneyValue {
return MoneyValue.createFromDinero(this.props.convertPrecision(newPrecision, roundingMode))
.object;
public convertScale(newScale: number, roundingMode?: RoundingMode): MoneyValue {
return MoneyValue.createFromDinero(this.props.convertPrecision(newScale, roundingMode)).object;
}
public getCurrency(): CurrencyData {
@ -322,7 +321,7 @@ export class MoneyValue extends ValueObject<Dinero> implements IMoneyValue {
const obj = this.props.toObject();
return {
amount: obj.amount,
precision: obj.precision,
scale: obj.precision,
currency_code: String(obj.currency),
};
}

View File

@ -5,22 +5,22 @@ import { Result } from "./Result";
export interface IUnitPriceProps {
amount: NullOr<number | string>;
currencyCode?: string;
precision: number;
scale: number;
}
export class UnitPrice extends MoneyValue {
public static create(props: IUnitPriceProps) {
const { amount, currencyCode, precision = 4 } = props;
const { amount, currencyCode, scale = 4 } = props;
const _unitPriceOrError = MoneyValue.create({
amount,
currencyCode,
precision,
scale,
});
if (_unitPriceOrError.isFailure) {
return _unitPriceOrError;
}
const _unitPrice = _unitPriceOrError.object.convertPrecision(4);
const _unitPrice = _unitPriceOrError.object.convertScale(4);
return Result.ok<UnitPrice>(_unitPrice);
}

View File

@ -60,7 +60,7 @@
"number.integer": "{{#label}}: debe ser un número entero",
"number.negative": "{{#label}}: debe ser un número negativo",
"number.positive": "{{#label}}: debe ser un número positivo",
"number.precision": "{{#label}}: no debe tener mas de {{limit}} decimales",
"number.scale": "{{#label}}: no debe tener mas de {{limit}} decimales",
"number.ref": "{{#label}}: referencia a \"{{ref}}\" que no es un número",
"number.multiple": "{{#label}}: debe ser un múltiplo de {{multiple}}",
"string.base": "{{#label}}: debe ser una cadena de texto",

View File

@ -1,6 +1,6 @@
export interface IListDealers_Response_DTO {
id: string;
name: string;
language: string;
lang_code: string;
status: string;
}

View File

@ -51,18 +51,18 @@ export function ensureUpdateQuote_Request_DTOIsValid(quoteDTO: IUpdateQuote_Requ
subtotal_price: Joi.object({
amount: Joi.number(),
precision: Joi.number(),
scale: Joi.number(),
currency_code: Joi.string(),
}).unknown(),
discount: Joi.object({
amount: Joi.number(),
precision: Joi.number(),
scale: Joi.number(),
}).unknown(),
total_price: Joi.object({
amount: Joi.number(),
precision: Joi.number(),
scale: Joi.number(),
currency_code: Joi.string(),
}).unknown(),
@ -71,26 +71,26 @@ export function ensureUpdateQuote_Request_DTOIsValid(quoteDTO: IUpdateQuote_Requ
article_id: Joi.string().optional().allow(null).allow("").default(""),
quantity: Joi.object({
amount: Joi.number(),
precision: Joi.number(),
scale: Joi.number(),
}).unknown(),
description: Joi.string(),
unit_price: Joi.object({
amount: Joi.number(),
precision: Joi.number(),
scale: Joi.number(),
currency_code: Joi.string(),
}).unknown(),
subtotal_price: Joi.object({
amount: Joi.number(),
precision: Joi.number(),
scale: Joi.number(),
currency_code: Joi.string(),
}).unknown(),
discount: Joi.object({
amount: Joi.number(),
precision: Joi.number(),
scale: Joi.number(),
}).unknown(),
total_price: Joi.object({
amount: Joi.number(),
precision: Joi.number(),
scale: Joi.number(),
currency_code: Joi.string(),
}).unknown(),
}).unknown(true)

View File

@ -1,5 +1,8 @@
import { IMoney_Response_DTO } from "shared/lib/contexts/common";
import { IPercentage_Response_DTO, IQuantity_Response_DTO } from "../../../../../common";
import {
IMoney_Response_DTO,
IPercentage_Response_DTO,
IQuantity_Response_DTO,
} from "../../../../../common";
export interface IUpdateQuote_Response_DTO {
id: string;