This commit is contained in:
David Arranz 2024-07-09 18:21:21 +02:00
parent 981d70cffe
commit 54dc76534a
15 changed files with 156 additions and 128 deletions

View File

@ -54,21 +54,21 @@ export const QuoteDetailsCardEditor = () => {
}, },
{ {
id: "retail_price" as const, id: "unit_price" as const,
accessorKey: "retail_price", accessorKey: "unit_price",
header: "retail_price", header: "unit_price",
size: 10, size: 10,
cell: ({ row: { index }, column: { id } }) => { cell: ({ row: { index }, column: { id } }) => {
return <FormMoneyField {...register(`items.${index}.retail_price`)} />; return <FormMoneyField {...register(`items.${index}.unit_price`)} />;
}, },
}, },
{ {
id: "price" as const, id: "subtotal_price" as const,
accessorKey: "price", accessorKey: "subtotal_price",
header: "price", header: "subtotal_price",
size: 10, size: 10,
cell: ({ row: { index }, column: { id } }) => { cell: ({ row: { index }, column: { id } }) => {
return <FormMoneyField {...register(`items.${index}.price`)} />; return <FormMoneyField {...register(`items.${index}.subtotal_price`)} />;
}, },
}, },
{ {
@ -81,12 +81,12 @@ export const QuoteDetailsCardEditor = () => {
}, },
}, },
{ {
id: "total" as const, id: "total_price" as const,
accessorKey: "total", accessorKey: "total_price",
header: "total", header: "total_price",
size: 10, size: 10,
cell: ({ row: { index }, column: { id } }) => { cell: ({ row: { index }, column: { id } }) => {
return <FormMoneyField {...register(`items.${index}.total`)} />; return <FormMoneyField {...register(`items.${index}.total_price`)} />;
}, },
}, },
], ],

View File

@ -33,7 +33,7 @@ interface QuoteDataForm extends IUpdateQuote_Request_DTO {
items: { items: {
quantity: IQuantity; quantity: IQuantity;
description: string; description: string;
retail_price: IMoney; unit_price: IMoney;
price: IMoney; price: IMoney;
discount: IPercentage; discount: IPercentage;
total: IMoney; total: IMoney;
@ -105,10 +105,10 @@ export const QuoteEdit = () => {
// Recálculo líneas // Recálculo líneas
items.map((item, index) => { items.map((item, index) => {
const itemTotals = calculateItemTotals(item); const itemTotals = calculateItemTotals(item);
quoteSubtotal = quoteSubtotal.add(itemTotals.total); quoteSubtotal = quoteSubtotal.add(itemTotals.totalPrice);
setValue(`items.${index}.price`, itemTotals.price.toObject()); setValue(`items.${index}.subtotal_price`, itemTotals.subtotalPrice.toObject());
setValue(`items.${index}.total`, itemTotals.total.toObject()); setValue(`items.${index}.total_price`, itemTotals.totalPrice.toObject());
}); });
console.log(quoteSubtotal.toFormat()); console.log(quoteSubtotal.toFormat());
@ -128,8 +128,8 @@ export const QuoteEdit = () => {
const itemTotals = calculateItemTotals(items[index]); const itemTotals = calculateItemTotals(items[index]);
setValue(`items.${index}.price`, itemTotals.price.toObject()); setValue(`items.${index}.subtotal_price`, itemTotals.subtotalPrice.toObject());
setValue(`items.${index}.total`, itemTotals.total.toObject()); setValue(`items.${index}.total_price`, itemTotals.totalPrice.toObject());
// Recálculo completo // Recálculo completo
} }
@ -168,9 +168,9 @@ export const QuoteEdit = () => {
</div> </div>
<FormMoneyField <FormMoneyField
label={"subtotal"} label={"subtotal_price"}
disabled={form.formState.disabled} disabled={form.formState.disabled}
{...form.register("subtotal")} {...form.register("subtotal_price")}
/> />
<Tabs defaultValue='items' className='space-y-4'> <Tabs defaultValue='items' className='space-y-4'>

View File

@ -3,57 +3,43 @@ import { IMoney, IPercentage, IQuantity } from "./types";
export const calculateItemTotals = (item: { export const calculateItemTotals = (item: {
quantity?: IQuantity; quantity?: IQuantity;
retail_price?: IMoney; unit_price?: IMoney;
discount?: IPercentage; discount?: IPercentage;
}): { }): {
quantity: Quantity; quantity: Quantity;
retailPrice: MoneyValue; unitPrice: MoneyValue;
price: MoneyValue; subtotalPrice: MoneyValue;
discount: Percentage; discount: Percentage;
total: MoneyValue; totalPrice: MoneyValue;
} => { } => {
const { quantity: quantityValue, retail_price: retailPriceValue, discount: discountValue } = item; const { quantity: quantity_value, unit_price: unit_price_value, discount: discount_value } = item;
console.log({ const quantityOrError = Quantity.create(quantity_value);
quantityValue,
retailPriceValue,
discountValue,
});
const quantityOrError = Quantity.create(quantityValue);
if (quantityOrError.isFailure) { if (quantityOrError.isFailure) {
throw quantityOrError.error; throw quantityOrError.error;
} }
const quantity = quantityOrError.object; const quantity = quantityOrError.object;
const retailPriceOrError = MoneyValue.create(retailPriceValue); const unitPriceOrError = MoneyValue.create(unit_price_value);
if (retailPriceOrError.isFailure) { if (unitPriceOrError.isFailure) {
throw retailPriceOrError.error; throw unitPriceOrError.error;
} }
const retailPrice = retailPriceOrError.object; const unitPrice = unitPriceOrError.object;
const discountOrError = Percentage.create(discountValue); const discountOrError = Percentage.create(discount_value);
if (discountOrError.isFailure) { if (discountOrError.isFailure) {
throw discountOrError.error; throw discountOrError.error;
} }
const discount = discountOrError.object; const discount = discountOrError.object;
const price = retailPrice.multiply(quantity.toNumber()); const subtotalPrice = unitPrice.multiply(quantity.toNumber());
const total = price.subtract(price.percentage(discount.toNumber())); const totalPrice = subtotalPrice.subtract(subtotalPrice.percentage(discount.toNumber()));
return { return {
quantity, quantity,
retailPrice, unitPrice,
price, subtotalPrice,
discount, discount,
total, totalPrice,
}; };
/*return {
quantity: quantity.toObject(),
retail_price: retailPrice.toObject(),
price: price.toObject(),
discount: discount.toObject(),
total: total.toObject(),
};*/
}; };

View File

@ -158,7 +158,6 @@ export abstract class SequelizeRepository<T> implements IRepository<T> {
}, },
transaction: this._transaction, transaction: this._transaction,
force, force,
logging: console.log,
}); });
} }

View File

@ -144,7 +144,9 @@ export class CreateQuoteUseCase
return Result.fail(referenceOrError.error); return Result.fail(referenceOrError.error);
} }
const languageOrError = Language.createFromCode(quoteDTO.lang_code); const languageOrError = Language.createFromCode(
quoteDTO.lang_code ?? this._dealer?.language.code
);
if (languageOrError.isFailure) { if (languageOrError.isFailure) {
return Result.fail(languageOrError.error); return Result.fail(languageOrError.error);
} }
@ -154,7 +156,9 @@ export class CreateQuoteUseCase
return Result.fail(customerOrError.error); return Result.fail(customerOrError.error);
} }
const currencyOrError = CurrencyData.createFromCode(quoteDTO.currency_code); const currencyOrError = CurrencyData.createFromCode(
quoteDTO.currency_code ?? CurrencyData.DEFAULT_CURRENCY_CODE
);
if (currencyOrError.isFailure) { if (currencyOrError.isFailure) {
return Result.fail(currencyOrError.error); return Result.fail(currencyOrError.error);
} }
@ -174,6 +178,11 @@ export class CreateQuoteUseCase
return Result.fail(validityOrError.error); return Result.fail(validityOrError.error);
} }
const discountOrError = Percentage.create(quoteDTO.discount);
if (discountOrError.isFailure) {
return Result.fail(discountOrError.error);
}
const items = new Collection<QuoteItem>( const items = new Collection<QuoteItem>(
quoteDTO.items?.map( quoteDTO.items?.map(
(item) => (item) =>
@ -182,11 +191,14 @@ export class CreateQuoteUseCase
description: Description.create(item.description).object, description: Description.create(item.description).object,
quantity: Quantity.create(item.quantity).object, quantity: Quantity.create(item.quantity).object,
unitPrice: UnitPrice.create({ unitPrice: UnitPrice.create({
amount: item.unit_price.amount, amount: item.unit_price?.amount,
currencyCode: item.unit_price.currency_code, currencyCode: item.unit_price?.currency_code,
precision: item.unit_price.precision, precision: item.unit_price?.precision,
}).object,
discount: Percentage.create({
amount: item.discount?.amount,
precision: item.discount?.precision,
}).object, }).object,
discount: Percentage.create(item.discount.amount).object,
}).object }).object
) )
); );
@ -203,6 +215,8 @@ export class CreateQuoteUseCase
notes: notesOrError.object, notes: notesOrError.object,
validity: validityOrError.object, validity: validityOrError.object,
discount: discountOrError.object,
items, items,
dealerId, dealerId,

View File

@ -9,12 +9,13 @@ import { IInfrastructureError } from "@/contexts/common/infrastructure";
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize"; import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
import { import {
Collection, Collection,
Currency, CurrencyData,
Description, Description,
DomainError, DomainError,
IDomainError, IDomainError,
Language, Language,
Note, Note,
Percentage,
Quantity, Quantity,
Result, Result,
UTCDateValue, UTCDateValue,
@ -23,11 +24,20 @@ import {
} from "@shared/contexts"; } from "@shared/contexts";
import { IUpdateQuote_Request_DTO } from "@shared/contexts"; import { IUpdateQuote_Request_DTO } from "@shared/contexts";
import { IQuoteRepository, Quote, QuoteCustomer, QuoteItem, QuoteStatus } from "../../domain"; import {
Dealer,
IQuoteRepository,
Quote,
QuoteCustomer,
QuoteItem,
QuoteReference,
QuoteStatus,
} from "../../domain";
import { ISalesContext } from "../../infrastructure";
export interface IUpdateQuoteUseCaseRequest extends IUseCaseRequest { export interface IUpdateQuoteUseCaseRequest extends IUseCaseRequest {
id: UniqueID; id: UniqueID;
QuoteDTO: IUpdateQuote_Request_DTO; quoteDTO: IUpdateQuote_Request_DTO;
} }
export type UpdateQuoteResponseOrError = export type UpdateQuoteResponseOrError =
@ -39,16 +49,26 @@ export class UpdateQuoteUseCase
{ {
private _adapter: ISequelizeAdapter; private _adapter: ISequelizeAdapter;
private _repositoryManager: IRepositoryManager; private _repositoryManager: IRepositoryManager;
private _dealer?: Dealer;
constructor(props: { adapter: ISequelizeAdapter; repositoryManager: IRepositoryManager }) { constructor(context: ISalesContext) {
this._adapter = props.adapter; this._adapter = context.adapter;
this._repositoryManager = props.repositoryManager; this._repositoryManager = context.repositoryManager;
this._dealer = context.dealer;
} }
async execute(request: IUpdateQuoteUseCaseRequest): Promise<UpdateQuoteResponseOrError> { async execute(request: IUpdateQuoteUseCaseRequest): Promise<UpdateQuoteResponseOrError> {
const { id, QuoteDTO } = request; const { id, quoteDTO } = request;
const QuoteRepository = this._getQuoteRepository(); const QuoteRepository = this._getQuoteRepository();
// 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;
// Comprobar que existe el Quote // Comprobar que existe el Quote
const idExists = await QuoteRepository().exists(id); const idExists = await QuoteRepository().exists(id);
if (!idExists) { if (!idExists) {
@ -61,10 +81,10 @@ export class UpdateQuoteUseCase
} }
// Crear usuario // Crear usuario
const QuoteOrError = this._tryCreateQuoteInstance(QuoteDTO, id); const quoteOrError = this._tryCreateQuoteInstance(quoteDTO, id, dealerId);
if (QuoteOrError.isFailure) { if (quoteOrError.isFailure) {
const { error: domainError } = QuoteOrError; const { error: domainError } = quoteOrError;
let errorCode = ""; let errorCode = "";
let message = ""; let message = "";
@ -84,22 +104,23 @@ export class UpdateQuoteUseCase
return Result.fail(UseCaseError.create(errorCode, message, domainError)); return Result.fail(UseCaseError.create(errorCode, message, domainError));
} }
return this._updateQuote(QuoteOrError.object); return this._updateQuote(quoteOrError.object);
} }
private async _updateQuote(Quote: Quote) { private async _updateQuote(quote: Quote) {
// Guardar el contacto // Guardar el contacto
const transaction = this._adapter.startTransaction(); const transaction = this._adapter.startTransaction();
const QuoteRepository = this._getQuoteRepository(); const quoteRepository = this._getQuoteRepository();
let QuoteRepo: IQuoteRepository; let quoteRepo: IQuoteRepository;
try { try {
await transaction.complete(async (t) => { await transaction.complete(async (t) => {
QuoteRepo = QuoteRepository({ transaction: t }); console.log(t);
await QuoteRepo.update(Quote); quoteRepo = quoteRepository({ transaction: t });
await quoteRepo.update(quote);
}); });
return Result.ok<Quote>(Quote); return Result.ok<Quote>(quote);
} catch (error: unknown) { } catch (error: unknown) {
const _error = error as IInfrastructureError; const _error = error as IInfrastructureError;
return Result.fail(UseCaseError.create(UseCaseError.REPOSITORY_ERROR, _error.message)); return Result.fail(UseCaseError.create(UseCaseError.REPOSITORY_ERROR, _error.message));
@ -108,7 +129,8 @@ export class UpdateQuoteUseCase
private _tryCreateQuoteInstance( private _tryCreateQuoteInstance(
quoteDTO: IUpdateQuote_Request_DTO, quoteDTO: IUpdateQuote_Request_DTO,
quoteId: UniqueID quoteId: UniqueID,
dealerId: UniqueID
): Result<Quote, IDomainError> { ): Result<Quote, IDomainError> {
const statusOrError = QuoteStatus.create(quoteDTO.status); const statusOrError = QuoteStatus.create(quoteDTO.status);
if (statusOrError.isFailure) { if (statusOrError.isFailure) {
@ -120,7 +142,7 @@ export class UpdateQuoteUseCase
return Result.fail(dateOrError.error); return Result.fail(dateOrError.error);
} }
const referenceOrError = QuoteStatus.create(quoteDTO.reference); const referenceOrError = QuoteReference.create(quoteDTO.reference);
if (referenceOrError.isFailure) { if (referenceOrError.isFailure) {
return Result.fail(referenceOrError.error); return Result.fail(referenceOrError.error);
} }
@ -135,7 +157,7 @@ export class UpdateQuoteUseCase
return Result.fail(customerOrError.error); return Result.fail(customerOrError.error);
} }
const currencyOrError = Currency.createFromCode(quoteDTO.currency_code); const currencyOrError = CurrencyData.createFromCode(quoteDTO.currency_code);
if (currencyOrError.isFailure) { if (currencyOrError.isFailure) {
return Result.fail(currencyOrError.error); return Result.fail(currencyOrError.error);
} }
@ -155,16 +177,26 @@ export class UpdateQuoteUseCase
return Result.fail(validityOrError.error); return Result.fail(validityOrError.error);
} }
const discountOrError = Percentage.create(quoteDTO.discount);
if (discountOrError.isFailure) {
return Result.fail(discountOrError.error);
}
const items = new Collection<QuoteItem>( const items = new Collection<QuoteItem>(
quoteDTO.items?.map( quoteDTO.items?.map(
(item) => (item) =>
QuoteItem.create({ QuoteItem.create({
articleId: item.article_id,
description: Description.create(item.description).object, description: Description.create(item.description).object,
quantity: Quantity.create({ amount: item.quantity, precision: 4 }).object, quantity: Quantity.create(item.quantity).object,
unitPrice: UnitPrice.create({ unitPrice: UnitPrice.create({
amount: item.unit_price.amount, amount: item.unit_price?.amount,
currencyCode: item.unit_price.currency, currencyCode: item.unit_price?.currency_code,
precision: item.unit_price.precision, precision: item.unit_price?.precision,
}).object,
discount: Percentage.create({
amount: item.discount?.amount,
precision: item.discount?.precision,
}).object, }).object,
}).object }).object
) )
@ -182,7 +214,11 @@ export class UpdateQuoteUseCase
notes: notesOrError.object, notes: notesOrError.object,
validity: validityOrError.object, validity: validityOrError.object,
discount: discountOrError.object,
items, items,
dealerId,
}, },
quoteId quoteId
); );

View File

@ -1,6 +1,6 @@
import { ISequelizeAdapter, SequelizeRepository } from "@/contexts/common/infrastructure/sequelize"; import { ISequelizeAdapter, SequelizeRepository } from "@/contexts/common/infrastructure/sequelize";
import { ICollection, IQueryCriteria, UniqueID } from "@shared/contexts"; import { ICollection, IQueryCriteria, UniqueID } from "@shared/contexts";
import { Transaction } from "sequelize"; import { ModelDefined, Transaction } from "sequelize";
import { IQuoteRepository } from "../domain"; import { IQuoteRepository } from "../domain";
import { Quote } from "../domain/entities"; import { Quote } from "../domain/entities";
@ -35,11 +35,27 @@ export class QuoteRepository extends SequelizeRepository<Quote> implements IQuot
} }
public async update(user: Quote): Promise<void> { public async update(user: Quote): Promise<void> {
console.time("update");
const userData = this.mapper.mapToPersistence(user); const userData = this.mapper.mapToPersistence(user);
// borrando y luego creando const QuoteItem_Model: ModelDefined<any, any> = this._adapter.getModel("QuoteItem_Model");
// await this.removeById(user.id, true);
await this._save("Quote_Model", user.id, userData, {}); await Promise.all([
this._save("Quote_Model", user.id, userData, {}),
QuoteItem_Model.destroy({
where: {
quote_id: userData.id,
},
transaction: this._transaction,
force: true,
}),
]);
await QuoteItem_Model.bulkCreate(userData.items, {
transaction: this._transaction,
});
console.timeEnd("update");
} }
public async getById(id: UniqueID): Promise<Quote | null> { public async getById(id: UniqueID): Promise<Quote | null> {

View File

@ -55,7 +55,7 @@ export default (sequelize: Sequelize) => {
}, },
id_article: { id_article: {
type: DataTypes.BIGINT().UNSIGNED, type: DataTypes.BIGINT().UNSIGNED,
allowNull: false, allowNull: true,
}, },
position: { position: {
type: new DataTypes.MEDIUMINT(), type: new DataTypes.MEDIUMINT(),

View File

@ -14,12 +14,12 @@ export const QuoteRouter = (appRouter: Express.Router) => {
quoteRoutes.get("/", checkUser, getDealerMiddleware, listQuotesController); quoteRoutes.get("/", checkUser, getDealerMiddleware, listQuotesController);
quoteRoutes.get("/:quoteId", checkUser, getDealerMiddleware, getQuoteController); quoteRoutes.get("/:quoteId", checkUser, getDealerMiddleware, getQuoteController);
quoteRoutes.post("/", checkUser, getDealerMiddleware, createQuoteController); quoteRoutes.post("/", checkUser, getDealerMiddleware, createQuoteController);
quoteRoutes.put("/:quoteId", checkUser, updateQuoteController); quoteRoutes.put("/:quoteId", checkUser, getDealerMiddleware, updateQuoteController);
/* /*
quoteRoutes.post("/", isAdmin, createQuoteController); quoteRoutes.post("/", isAdmin, createQuoteController);
quoteRoutes.delete("/:quoteId", isAdmin, deleteQuoteController);*/ quoteRoutes.delete("/:quoteId", isAdmin, getDealerMiddleware, deleteQuoteController);*/
appRouter.use("/quotes", quoteRoutes); appRouter.use("/quotes", quoteRoutes);
}; };

View File

@ -1,6 +1,7 @@
import Joi from "joi"; import Joi from "joi";
import { isNull } from "lodash"; import { isNull } from "lodash";
import { NullOr } from "../../../../utilities"; import { NullOr } from "../../../../utilities";
import { DomainError, handleDomainError } from "../errors";
import { RuleValidator } from "../RuleValidator"; import { RuleValidator } from "../RuleValidator";
import { INullableValueObjectOptions, NullableValueObject } from "./NullableValueObject"; import { INullableValueObjectOptions, NullableValueObject } from "./NullableValueObject";
import { Result } from "./Result"; import { Result } from "./Result";
@ -70,7 +71,9 @@ export class Percentage extends NullableValueObject<IPercentage> {
const validationResult = Percentage.validate(amount, _options); const validationResult = Percentage.validate(amount, _options);
if (validationResult.isFailure) { if (validationResult.isFailure) {
return Result.fail(validationResult.error); return Result.fail(
handleDomainError(DomainError.INVALID_INPUT_DATA, validationResult.error.message, _options)
);
} }
let _amount: NullOr<number> = Percentage.sanitize(validationResult.object); let _amount: NullOr<number> = Percentage.sanitize(validationResult.object);

View File

@ -74,7 +74,6 @@ export class Quantity extends NullableValueObject<IQuantity> {
} }
let _amount: NullOr<number> = Quantity.sanitize(validationResult.object); let _amount: NullOr<number> = Quantity.sanitize(validationResult.object);
const _props = { const _props = {
amount: isNull(_amount) ? 0 : _amount, amount: isNull(_amount) ? 0 : _amount,
precision, precision,

View File

@ -1,6 +1,6 @@
import { NullOr } from '../../../../utilities'; import { NullOr } from "../../../../utilities";
import { MoneyValue } from './MoneyValue'; import { MoneyValue } from "./MoneyValue";
import { Result } from './Result'; import { Result } from "./Result";
export interface IUnitPriceProps { export interface IUnitPriceProps {
amount: NullOr<number | string>; amount: NullOr<number | string>;
@ -10,8 +10,7 @@ export interface IUnitPriceProps {
export class UnitPrice extends MoneyValue { export class UnitPrice extends MoneyValue {
public static create(props: IUnitPriceProps) { public static create(props: IUnitPriceProps) {
const {amount, currencyCode, precision = 4} = props; const { amount, currencyCode, precision = 4 } = props;
const _unitPriceOrError = MoneyValue.create({ const _unitPriceOrError = MoneyValue.create({
amount, amount,
currencyCode, currencyCode,

View File

@ -22,7 +22,7 @@ export * from "./StringValueObject";
export * from "./TextValueObject"; export * from "./TextValueObject";
export * from "./TINNumber"; export * from "./TINNumber";
export * from "./UniqueID"; export * from "./UniqueID";
//export * from "./UnitPrice"; export * from "./UnitPrice";
export * from "./UTCDateValue"; export * from "./UTCDateValue";
export * from "./ValueObject"; export * from "./ValueObject";

View File

@ -41,34 +41,10 @@ export interface ICreateQuoteItem_Request_DTO {
export function ensureCreateQuote_Request_DTOIsValid(quoteDTO: ICreateQuote_Request_DTO) { export function ensureCreateQuote_Request_DTOIsValid(quoteDTO: ICreateQuote_Request_DTO) {
const schema = Joi.object({ const schema = Joi.object({
id: Joi.string(), id: Joi.string(),
status: Joi.string(),
date: Joi.string(), date: Joi.string(),
reference: Joi.string(), reference: Joi.string(),
lang_code: Joi.string(),
customer_information: Joi.string(), customer_information: Joi.string(),
currency_code: Joi.string(),
payment_method: Joi.string(),
notes: Joi.string(),
validity: Joi.string(),
items: Joi.array().items(
Joi.object({
article_id: Joi.string(),
description: Joi.string(),
quantity: {
amount: Joi.number(),
precision: Joi.number(),
},
unit_price: Joi.object({
amount: Joi.number(),
precision: Joi.number(),
currency: Joi.string(),
}),
discount: Joi.object({
amount: Joi.number(),
precision: Joi.number(),
}),
}).unknown(true)
),
}).unknown(true); }).unknown(true);
const result = RuleValidator.validate<ICreateQuote_Request_DTO>(schema, quoteDTO); const result = RuleValidator.validate<ICreateQuote_Request_DTO>(schema, quoteDTO);

View File

@ -43,14 +43,14 @@ export function ensureUpdateQuote_Request_DTOIsValid(quoteDTO: IUpdateQuote_Requ
customer_information: Joi.string(), customer_information: Joi.string(),
lang_code: Joi.string(), lang_code: Joi.string(),
currency_code: Joi.string(), currency_code: Joi.string(),
payment_method: Joi.string(), payment_method: Joi.string().optional().allow(null).allow("").default(""),
notes: Joi.string(), notes: Joi.string().optional().allow(null).allow("").default(""),
validity: Joi.string(), validity: Joi.string().optional().allow(null).allow("").default(""),
subtotal: Joi.object({ subtotal: Joi.object({
amount: Joi.number(), amount: Joi.number(),
precision: Joi.number(), precision: Joi.number(),
currency: Joi.string(), currency_code: Joi.string(),
}), }),
discount: Joi.object({ discount: Joi.object({
@ -69,12 +69,12 @@ export function ensureUpdateQuote_Request_DTOIsValid(quoteDTO: IUpdateQuote_Requ
unit_price: Joi.object({ unit_price: Joi.object({
amount: Joi.number(), amount: Joi.number(),
precision: Joi.number(), precision: Joi.number(),
currency: Joi.string(), currency_code: Joi.string(),
}), }),
subtotal_price: Joi.object({ subtotal_price: Joi.object({
amount: Joi.number(), amount: Joi.number(),
precision: Joi.number(), precision: Joi.number(),
currency: Joi.string(), currency_code: Joi.string(),
}), }),
discount: Joi.object({ discount: Joi.object({
amount: Joi.number(), amount: Joi.number(),
@ -83,7 +83,7 @@ export function ensureUpdateQuote_Request_DTOIsValid(quoteDTO: IUpdateQuote_Requ
total_price: Joi.object({ total_price: Joi.object({
amount: Joi.number(), amount: Joi.number(),
precision: Joi.number(), precision: Joi.number(),
currency: Joi.string(), currency_code: Joi.string(),
}), }),
}).unknown(true) }).unknown(true)
), ),