This commit is contained in:
David Arranz 2024-07-02 22:15:59 +02:00
parent 0b00b84289
commit 985111af5f
16 changed files with 153 additions and 39 deletions

View File

@ -0,0 +1,32 @@
import { IRepositoryManager, RepositoryManager } from "@/contexts/common/domain";
import {
ISequelizeAdapter,
createSequelizeAdapter,
} from "@/contexts/common/infrastructure/sequelize";
export interface ICatalogContext {
adapter: ISequelizeAdapter;
repositoryManager: IRepositoryManager;
//services: IApplicationService;
}
export class CatalogContext {
private static instance: CatalogContext | null = null;
public static getInstance(): ICatalogContext {
if (!CatalogContext.instance) {
CatalogContext.instance = new CatalogContext({
adapter: createSequelizeAdapter(),
repositoryManager: RepositoryManager.getInstance(),
});
}
return CatalogContext.instance.context;
}
private context: ICatalogContext;
private constructor(context: ICatalogContext) {
this.context = context;
}
}

View File

@ -45,7 +45,7 @@ export class Article_Model extends Model<
declare points: CreationOptional<number>; declare points: CreationOptional<number>;
declare retail_price: CreationOptional<number>; declare retail_price: CreationOptional<number>;
declare translations?: NonAttribute<ArticleTranslation_Model[]>; declare translations: NonAttribute<ArticleTranslation_Model[]>;
} }
export default (sequelize: Sequelize) => { export default (sequelize: Sequelize) => {

View File

@ -43,6 +43,7 @@ export class ListQuotesUseCase implements IUseCase<IListQuotesParams, Promise<Li
return Result.ok(Quotes); return Result.ok(Quotes);
} catch (error: unknown) { } catch (error: unknown) {
const _error = error as IInfrastructureError; const _error = error as IInfrastructureError;
console.trace(_error.message);
return Result.fail( return Result.fail(
UseCaseError.create( UseCaseError.create(
UseCaseError.REPOSITORY_ERROR, UseCaseError.REPOSITORY_ERROR,

View File

@ -14,6 +14,7 @@ import {
DomainError, DomainError,
IDomainError, IDomainError,
Language, Language,
Note,
Quantity, Quantity,
Result, Result,
UTCDateValue, UTCDateValue,
@ -22,7 +23,7 @@ import {
} from "@shared/contexts"; } from "@shared/contexts";
import { IUpdateQuote_Request_DTO } from "@shared/contexts"; import { IUpdateQuote_Request_DTO } from "@shared/contexts";
import { IQuoteRepository, Quote, QuoteItem, QuoteStatus } from "../../domain"; import { IQuoteRepository, Quote, QuoteCustomer, QuoteItem, QuoteStatus } from "../../domain";
export interface IUpdateQuoteUseCaseRequest extends IUseCaseRequest { export interface IUpdateQuoteUseCaseRequest extends IUseCaseRequest {
id: UniqueID; id: UniqueID;
@ -119,22 +120,47 @@ export class UpdateQuoteUseCase
return Result.fail(dateOrError.error); return Result.fail(dateOrError.error);
} }
const languageOrError = Language.createFromCode(quoteDTO.language_code); const referenceOrError = QuoteStatus.create(quoteDTO.reference);
if (referenceOrError.isFailure) {
return Result.fail(referenceOrError.error);
}
const languageOrError = Language.createFromCode(quoteDTO.lang_code);
if (languageOrError.isFailure) { if (languageOrError.isFailure) {
return Result.fail(languageOrError.error); return Result.fail(languageOrError.error);
} }
const customerOrError = QuoteCustomer.create(quoteDTO.customer_information);
if (customerOrError.isFailure) {
return Result.fail(customerOrError.error);
}
const currencyOrError = Currency.createFromCode(quoteDTO.currency_code); const currencyOrError = Currency.createFromCode(quoteDTO.currency_code);
if (currencyOrError.isFailure) { if (currencyOrError.isFailure) {
return Result.fail(currencyOrError.error); return Result.fail(currencyOrError.error);
} }
const paymentOrError = Note.create(quoteDTO.payment_method);
if (paymentOrError.isFailure) {
return Result.fail(paymentOrError.error);
}
const notesOrError = Note.create(quoteDTO.notes);
if (notesOrError.isFailure) {
return Result.fail(notesOrError.error);
}
const validityOrError = Note.create(quoteDTO.validity);
if (validityOrError.isFailure) {
return Result.fail(validityOrError.error);
}
const items = new Collection<QuoteItem>( const items = new Collection<QuoteItem>(
quoteDTO.items?.map( quoteDTO.items?.map(
(item) => (item) =>
QuoteItem.create({ QuoteItem.create({
description: Description.create(item.description).object, description: Description.create(item.description).object,
quantity: Quantity.create(item.quantity).object, quantity: Quantity.create({ amount: item.quantity, precision: 4 }).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,
@ -148,8 +174,14 @@ export class UpdateQuoteUseCase
{ {
status: statusOrError.object, status: statusOrError.object,
date: dateOrError.object, date: dateOrError.object,
reference: referenceOrError.object,
language: languageOrError.object, language: languageOrError.object,
customer: customerOrError.object,
currency: currencyOrError.object, currency: currencyOrError.object,
paymentMethod: paymentOrError.object,
notes: notesOrError.object,
validity: validityOrError.object,
items, items,
}, },
quoteId quoteId

View File

@ -26,6 +26,8 @@ export interface IQuoteProps {
validity: Note; validity: Note;
items: ICollection<QuoteItem>; items: ICollection<QuoteItem>;
dealerId: UniqueID;
} }
export interface IQuote { export interface IQuote {
@ -41,6 +43,8 @@ export interface IQuote {
notes: Note; notes: Note;
validity: Note; validity: Note;
items: ICollection<QuoteItem>; items: ICollection<QuoteItem>;
dealerId: UniqueID;
} }
export class Quote extends AggregateRoot<IQuoteProps> implements IQuote { export class Quote extends AggregateRoot<IQuoteProps> implements IQuote {
@ -105,4 +109,8 @@ export class Quote extends AggregateRoot<IQuoteProps> implements IQuote {
get items() { get items() {
return this._items; return this._items;
} }
get dealerId() {
return this.dealerId;
}
} }

View File

@ -1,10 +1,16 @@
import { ListQuotesUseCase } from "@/contexts/sales/application"; import { ListQuotesUseCase } from "@/contexts/sales/application";
import { registerQuoteRepository } from "@/contexts/sales/infrastructure/Quote.repository"; import { registerQuoteRepository } from "@/contexts/sales/infrastructure/Quote.repository";
import { ISalesContext } from "../../../../Sales.context"; import Express from "express";
import { ListQuotesController } from "./ListQuotes.controller"; import { ListQuotesController } from "./ListQuotes.controller";
import { ListQuotesPresenter } from "./presenter"; import { ListQuotesPresenter } from "./presenter";
export const listQuotesController = (context: ISalesContext) => { export const listQuotesController = (
req: Express.Request,
res: Express.Response,
next: Express.NextFunction
) => {
const context = res.locals.context;
registerQuoteRepository(context); registerQuoteRepository(context);
return new ListQuotesController( return new ListQuotesController(
@ -13,5 +19,5 @@ export const listQuotesController = (context: ISalesContext) => {
presenter: ListQuotesPresenter, presenter: ListQuotesPresenter,
}, },
context context
); ).execute(req, res, next);
}; };

View File

@ -21,8 +21,11 @@ export const ListQuotesPresenter: IListQuotesPresenter = {
id: quote.id.toString(), id: quote.id.toString(),
status: quote.status.toString(), status: quote.status.toString(),
date: quote.date.toString(), date: quote.date.toString(),
language_code: quote.date.toISO8601(), reference: quote.reference.toString(),
customer_information: quote.customer.toString(),
lang_code: quote.date.toISO8601(),
currency_code: quote.currency.toString(), currency_code: quote.currency.toString(),
subtotal: { subtotal: {
amount: 0, amount: 0,
precision: 2, precision: 2,

View File

@ -1,7 +1,7 @@
import { Currency, Language, UTCDateValue, UniqueID } from "@shared/contexts"; import { Currency, Language, Note, UTCDateValue, UniqueID } from "@shared/contexts";
import { ISequelizeMapper, SequelizeMapper } from "@/contexts/common/infrastructure"; import { ISequelizeMapper, SequelizeMapper } from "@/contexts/common/infrastructure";
import { IQuoteProps, Quote } from "../../domain"; import { IQuoteProps, Quote, QuoteCustomer, QuoteReference } from "../../domain";
import { QuoteStatus } from "../../domain/entities/Quotes/QuoteStatus"; import { QuoteStatus } from "../../domain/entities/Quotes/QuoteStatus";
import { ISalesContext } from "../Sales.context"; import { ISalesContext } from "../Sales.context";
import { QuoteCreationAttributes, Quote_Model } from "../sequelize"; import { QuoteCreationAttributes, Quote_Model } from "../sequelize";
@ -34,11 +34,18 @@ class QuoteMapper
const props: IQuoteProps = { const props: IQuoteProps = {
status: this.mapsValue(source, "status", QuoteStatus.create), status: this.mapsValue(source, "status", QuoteStatus.create),
date: this.mapsValue(source, "issue_date", UTCDateValue.create), date: this.mapsValue(source, "issue_date", UTCDateValue.create),
reference: this.mapsValue(source, "reference", QuoteReference.create),
currency: this.mapsValue(source, "quote_currency", Currency.createFromCode), currency: this.mapsValue(source, "quote_currency", Currency.createFromCode),
language: this.mapsValue(source, "quote_language", Language.createFromCode), language: this.mapsValue(source, "quote_language", Language.createFromCode),
customer: source.customer_information, customer: this.mapsValue(source, "customer", QuoteCustomer.create),
validity: this.mapsValue(source, "validity", Note.create),
paymentMethod: this.mapsValue(source, "paymentMethod", Note.create),
notes: this.mapsValue(source, "notes", Note.create),
items, items,
dealerId: this.mapsValue(source, "dealer_id", UniqueID.create),
}; };
const quoteOrError = Quote.create(props, id); const quoteOrError = Quote.create(props, id);
@ -60,12 +67,20 @@ class QuoteMapper
id: source.id.toPrimitive(), id: source.id.toPrimitive(),
status: source.status.toPrimitive(), status: source.status.toPrimitive(),
date: source.date.toPrimitive(), date: source.date.toPrimitive(),
reference: source.reference.toPrimitive(),
currency_code: source.currency.toPrimitive(), currency_code: source.currency.toPrimitive(),
lang_code: source.language.toPrimitive(), lang_code: source.language.toPrimitive(),
customer_information: source.customer, customer_information: source.customer.toPrimitive(),
validity: source.validity.toPrimitive(),
payment_method: source.paymentMethod.toPrimitive(),
notes: source.notes.toPrimitive(),
discount: 0,
subtotal: 0, subtotal: 0,
total: 0, total: 0,
items, items,
dealer_id: source.dealerId.toPrimitive(),
}; };
return quote; return quote;

View File

@ -1,4 +1,4 @@
import { User_Model } from "@/contexts/users"; import { UserCreationAttributes, User_Model } from "@/contexts/users";
import { import {
DataTypes, DataTypes,
InferAttributes, InferAttributes,
@ -8,12 +8,15 @@ import {
Op, Op,
Sequelize, Sequelize,
} from "sequelize"; } from "sequelize";
import { Quote_Model } from "./quote.model"; import { QuoteCreationAttributes, Quote_Model } from "./quote.model";
export type DealerCreationAttributes = InferCreationAttributes< export type DealerCreationAttributes = InferCreationAttributes<
Dealer_Model, Dealer_Model,
{ omit: "user" | "quotes" } { omit: "user" | "quotes" }
>; > & {
user: UserCreationAttributes;
quotes: QuoteCreationAttributes[];
};
export class Dealer_Model extends Model< export class Dealer_Model extends Model<
InferAttributes<Dealer_Model, { omit: "user" | "quotes" }>, InferAttributes<Dealer_Model, { omit: "user" | "quotes" }>,
@ -52,8 +55,8 @@ export class Dealer_Model extends Model<
declare status: string; declare status: string;
declare language: string; declare language: string;
declare user?: NonAttribute<User_Model>; declare user: NonAttribute<User_Model>;
declare quotes?: NonAttribute<Quote_Model>; declare quotes: NonAttribute<Quote_Model>;
} }
export default (sequelize: Sequelize) => { export default (sequelize: Sequelize) => {

View File

@ -9,12 +9,15 @@ import {
Sequelize, Sequelize,
} from "sequelize"; } from "sequelize";
import { Dealer_Model } from "./dealer.model"; import { Dealer_Model } from "./dealer.model";
import { QuoteItem_Model } from "./quoteItem.model"; import { QuoteItemCreationAttributes, QuoteItem_Model } from "./quoteItem.model";
export type QuoteCreationAttributes = InferCreationAttributes< export type QuoteCreationAttributes = InferCreationAttributes<
Quote_Model, Quote_Model,
{ omit: "items" | "dealer" } { omit: "items" | "dealer" }
>; > & {
items: QuoteItemCreationAttributes[];
dealer_id: string;
};
export class Quote_Model extends Model< export class Quote_Model extends Model<
InferAttributes<Quote_Model, { omit: "items" | "dealer" }>, InferAttributes<Quote_Model, { omit: "items" | "dealer" }>,
@ -51,8 +54,8 @@ export class Quote_Model extends Model<
declare discount: CreationOptional<number>; declare discount: CreationOptional<number>;
declare total: CreationOptional<number>; declare total: CreationOptional<number>;
declare items?: NonAttribute<QuoteItem_Model[]>; declare items: NonAttribute<QuoteItem_Model[]>;
declare dealer?: NonAttribute<Dealer_Model>; declare dealer: NonAttribute<Dealer_Model>;
} }
export default (sequelize: Sequelize) => { export default (sequelize: Sequelize) => {

View File

@ -37,7 +37,7 @@ export class QuoteItem_Model extends Model<
declare subtotal: CreationOptional<number>; declare subtotal: CreationOptional<number>;
declare total: CreationOptional<number>; declare total: CreationOptional<number>;
declare quote?: NonAttribute<Quote_Model>; declare quote: NonAttribute<Quote_Model>;
} }
export default (sequelize: Sequelize) => { export default (sequelize: Sequelize) => {

View File

@ -1,3 +1,7 @@
import { UserContext } from "@/contexts/users/infrastructure/User.context"; import { RepositoryManager } from "@/contexts/common/domain";
import { createSequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
export const createContextMiddleware = () => UserContext.getInstance(); export const createContextMiddleware = () => ({
adapter: createSequelizeAdapter(),
repositoryManager: RepositoryManager.getInstance(),
});

View File

@ -8,13 +8,7 @@ import Express from "express";
export const QuoteRouter = (appRouter: Express.Router) => { export const QuoteRouter = (appRouter: Express.Router) => {
const quoteRoutes: Express.Router = Express.Router({ mergeParams: true }); const quoteRoutes: Express.Router = Express.Router({ mergeParams: true });
quoteRoutes.get( quoteRoutes.get("/", checkUser, listQuotesController);
"/",
checkUser,
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
listQuotesController(res.locals["context"]).execute(req, res, next)
);
quoteRoutes.post("/", checkUser, createQuoteController); quoteRoutes.post("/", checkUser, createQuoteController);
//quoteRoutes.put("/:quoteId", checkUser, updateQuoteController); //quoteRoutes.put("/:quoteId", checkUser, updateQuoteController);

View File

@ -2,10 +2,10 @@ import Express from "express";
import { createContextMiddleware } from "./context.middleware"; import { createContextMiddleware } from "./context.middleware";
import { import {
DealerRouter, DealerRouter,
QuoteRouter,
authRouter, authRouter,
catalogRouter, catalogRouter,
profileRouter, profileRouter,
quoteRoutes,
usersRouter, usersRouter,
} from "./routes"; } from "./routes";
@ -22,6 +22,7 @@ export const v1Routes = () => {
return next(); return next();
}); });
routes.use((req, res, next) => { routes.use((req, res, next) => {
console.log(`[${new Date().toLocaleTimeString()}] Incoming request to ${req.path}`); console.log(`[${new Date().toLocaleTimeString()}] Incoming request to ${req.path}`);
next(); next();
@ -32,7 +33,7 @@ export const v1Routes = () => {
usersRouter(routes); usersRouter(routes);
catalogRouter(routes); catalogRouter(routes);
DealerRouter(routes); DealerRouter(routes);
quoteRoutes(routes); QuoteRouter(routes);
return routes; return routes;
}; };

View File

@ -1,11 +1,12 @@
import { IMoney_Response_DTO } from "shared/lib/contexts/common"; import { IMoney_Response_DTO } from "../../../../../common";
export interface IListQuotes_Response_DTO { export interface IListQuotes_Response_DTO {
id: string; id: string;
status: string; status: string;
date: string; date: string;
language_code: string; reference: string;
customer_information: string;
lang_code: string;
currency_code: string; currency_code: string;
subtotal: IMoney_Response_DTO; subtotal: IMoney_Response_DTO;

View File

@ -4,8 +4,13 @@ import { IMoney_Request_DTO, Result, RuleValidator } from "../../../../../common
export interface IUpdateQuote_Request_DTO { export interface IUpdateQuote_Request_DTO {
status: string; status: string;
date: string; date: string;
language_code: string; reference: string;
customer_information: string;
lang_code: string;
currency_code: string; currency_code: string;
payment_method: string;
notes: string;
validity: string;
items: IUpdateQuoteItem_Request_DTO[]; items: IUpdateQuoteItem_Request_DTO[];
} }
@ -20,8 +25,14 @@ export interface IUpdateQuoteItem_Request_DTO {
export function ensureUpdateQuote_Request_DTOIsValid(quoteDTO: IUpdateQuote_Request_DTO) { export function ensureUpdateQuote_Request_DTOIsValid(quoteDTO: IUpdateQuote_Request_DTO) {
const schema = Joi.object({ const schema = Joi.object({
date: Joi.string(), date: Joi.string(),
language: Joi.string(), reference: Joi.string(),
currency: Joi.string(), lang_code: Joi.string(),
customer_information: Joi.string(),
currency_code: Joi.string(),
payment_method: Joi.string(),
notes: Joi.string(),
validity: Joi.string(),
items: Joi.array().items( items: Joi.array().items(
Joi.object({ Joi.object({
description: Joi.string(), description: Joi.string(),