From 220d57cb7b7ffe69f44250ee61fcdfb6fd00cabd Mon Sep 17 00:00:00 2001 From: david Date: Sat, 7 Mar 2026 23:11:39 +0100 Subject: [PATCH] . --- .../di/customer-snapshot-builders.di.ts | 25 ++------- .../application/di/customer-use-cases.di.ts | 11 ++-- .../application/models/customer-summary.ts | 4 +- .../application/presenters/domain/index.ts | 1 - .../domain/customer-snapshot-builder.ts} | 19 ++++--- .../domain/customer-snapshot.interface.ts | 39 ++++++++++++++ .../snapshot-builders/domain/index.ts | 2 + .../application/snapshot-builders/index.ts | 3 +- .../customer-summary-snapshot-builder.ts | 2 +- .../create/create-customer.use-case.ts | 20 ++++--- .../use-cases/get-customer-by-id.use-case.ts | 50 +++++++++++++++++ .../use-cases/get-customer.use-case.ts | 54 ------------------- .../src/api/application/use-cases/index.ts | 2 +- .../update/update-customer.use-case.ts | 16 +++--- .../src/api/infrastructure/di/customers.di.ts | 8 +-- .../express/customers.routes.ts | 10 ++-- .../sequelize-customer-summary.mapper.ts | 2 +- 17 files changed, 150 insertions(+), 118 deletions(-) delete mode 100644 modules/customers/src/api/application/presenters/domain/index.ts rename modules/customers/src/api/application/{presenters/domain/customer.full.presenter.ts => snapshot-builders/domain/customer-snapshot-builder.ts} (85%) create mode 100644 modules/customers/src/api/application/snapshot-builders/domain/customer-snapshot.interface.ts create mode 100644 modules/customers/src/api/application/snapshot-builders/domain/index.ts create mode 100644 modules/customers/src/api/application/use-cases/get-customer-by-id.use-case.ts delete mode 100644 modules/customers/src/api/application/use-cases/get-customer.use-case.ts diff --git a/modules/customers/src/api/application/di/customer-snapshot-builders.di.ts b/modules/customers/src/api/application/di/customer-snapshot-builders.di.ts index db028b82..d45dc123 100644 --- a/modules/customers/src/api/application/di/customer-snapshot-builders.di.ts +++ b/modules/customers/src/api/application/di/customer-snapshot-builders.di.ts @@ -1,32 +1,13 @@ // application/issued-invoices/di/snapshot-builders.di.ts -import { CustomerSummarySnapshotBuilder } from "../snapshot-builders"; +import { CustomerFullSnapshotBuilder, CustomerSummarySnapshotBuilder } from "../snapshot-builders"; export function buildCustomerSnapshotBuilders() { - /*const itemsBuilder = new CustomerItemsFullSnapshotBuilder(); - - const taxesBuilder = new CustomerTaxesFullSnapshotBuilder(); - - const recipientBuilder = new CustomerRecipientFullSnapshotBuilder(); - - const fullSnapshotBuilder = new CustomerFullSnapshotBuilder( - itemsBuilder, - recipientBuilder, - taxesBuilder - );*/ - + const fullSnapshotBuilder = new CustomerFullSnapshotBuilder(); const summarySnapshotBuilder = new CustomerSummarySnapshotBuilder(); - /*const itemsReportBuilder = new CustomerItemReportSnapshotBuilder(); - const taxesReportBuilder = new CustomerTaxReportSnapshotBuilder(); - const reportSnapshotBuilder = new CustomerReportSnapshotBuilder( - itemsReportBuilder, - taxesReportBuilder - );*/ - return { - //full: fullSnapshotBuilder, + full: fullSnapshotBuilder, summary: summarySnapshotBuilder, - //report: reportSnapshotBuilder, }; } diff --git a/modules/customers/src/api/application/di/customer-use-cases.di.ts b/modules/customers/src/api/application/di/customer-use-cases.di.ts index c163edb7..20234edb 100644 --- a/modules/customers/src/api/application/di/customer-use-cases.di.ts +++ b/modules/customers/src/api/application/di/customer-use-cases.di.ts @@ -1,16 +1,19 @@ import type { ITransactionManager } from "@erp/core/api"; import type { ICustomerFinder } from "../services"; -import type { ICustomerSummarySnapshotBuilder } from "../snapshot-builders"; -import { ListCustomersUseCase } from "../use-cases"; +import type { + ICustomerFullSnapshotBuilder, + ICustomerSummarySnapshotBuilder, +} from "../snapshot-builders"; +import { GetCustomerByIdUseCase, ListCustomersUseCase } from "../use-cases"; -/*export function buildGetCustomerByIdUseCase(deps: { +export function buildGetCustomerByIdUseCase(deps: { finder: ICustomerFinder; fullSnapshotBuilder: ICustomerFullSnapshotBuilder; transactionManager: ITransactionManager; }) { return new GetCustomerByIdUseCase(deps.finder, deps.fullSnapshotBuilder, deps.transactionManager); -}*/ +} export function buildListCustomersUseCase(deps: { finder: ICustomerFinder; diff --git a/modules/customers/src/api/application/models/customer-summary.ts b/modules/customers/src/api/application/models/customer-summary.ts index f34879cc..947d3570 100644 --- a/modules/customers/src/api/application/models/customer-summary.ts +++ b/modules/customers/src/api/application/models/customer-summary.ts @@ -11,12 +11,10 @@ import type { } from "@repo/rdx-ddd"; import type { Maybe } from "@repo/rdx-utils"; -import type { CustomerStatus } from "../../domain"; - export type CustomerSummary = { id: UniqueID; companyId: UniqueID; - status: CustomerStatus; + isActive: boolean; reference: Maybe; isCompany: boolean; diff --git a/modules/customers/src/api/application/presenters/domain/index.ts b/modules/customers/src/api/application/presenters/domain/index.ts deleted file mode 100644 index f2d46ce5..00000000 --- a/modules/customers/src/api/application/presenters/domain/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./customer.full.presenter"; diff --git a/modules/customers/src/api/application/presenters/domain/customer.full.presenter.ts b/modules/customers/src/api/application/snapshot-builders/domain/customer-snapshot-builder.ts similarity index 85% rename from modules/customers/src/api/application/presenters/domain/customer.full.presenter.ts rename to modules/customers/src/api/application/snapshot-builders/domain/customer-snapshot-builder.ts index 0bbebc8c..b349aba4 100644 --- a/modules/customers/src/api/application/presenters/domain/customer.full.presenter.ts +++ b/modules/customers/src/api/application/snapshot-builders/domain/customer-snapshot-builder.ts @@ -1,24 +1,26 @@ -import { Presenter } from "@erp/core/api"; +import type { ISnapshotBuilder } from "@erp/core/api"; import { maybeToEmptyString } from "@repo/rdx-ddd"; -import type { GetCustomerByIdResponseDTO } from "../../../../common/dto"; import type { Customer } from "../../../domain"; -export class CustomerFullPresenter extends Presenter { - toOutput(customer: Customer): GetCustomerByIdResponseDTO { +import type { ICustomerFullSnapshot } from "./customer-snapshot.interface"; + +export interface ICustomerFullSnapshotBuilder + extends ISnapshotBuilder {} + +export class CustomerFullSnapshotBuilder implements ICustomerFullSnapshotBuilder { + toOutput(customer: Customer): ICustomerFullSnapshot { const address = customer.address.toPrimitive(); return { id: customer.id.toPrimitive(), company_id: customer.companyId.toPrimitive(), - + status: customer.isActive ? "active" : "inactive", reference: maybeToEmptyString(customer.reference, (value) => value.toPrimitive()), is_company: String(customer.isCompany), name: customer.name.toPrimitive(), - trade_name: maybeToEmptyString(customer.tradeName, (value) => value.toPrimitive()), - tin: maybeToEmptyString(customer.tin, (value) => value.toPrimitive()), street: maybeToEmptyString(address.street, (value) => value.toPrimitive()), @@ -30,8 +32,10 @@ export class CustomerFullPresenter extends Presenter value.toPrimitive()), email_secondary: maybeToEmptyString(customer.emailSecondary, (value) => value.toPrimitive()), + phone_primary: maybeToEmptyString(customer.phonePrimary, (value) => value.toPrimitive()), phone_secondary: maybeToEmptyString(customer.phoneSecondary, (value) => value.toPrimitive()), + mobile_primary: maybeToEmptyString(customer.mobilePrimary, (value) => value.toPrimitive()), mobile_secondary: maybeToEmptyString(customer.mobileSecondary, (value) => value.toPrimitive() @@ -44,7 +48,6 @@ export class CustomerFullPresenter extends Presenter tax.toString()), - status: customer.isActive ? "active" : "inactive", language_code: customer.languageCode.toPrimitive(), currency_code: customer.currencyCode.toPrimitive(), diff --git a/modules/customers/src/api/application/snapshot-builders/domain/customer-snapshot.interface.ts b/modules/customers/src/api/application/snapshot-builders/domain/customer-snapshot.interface.ts new file mode 100644 index 00000000..22508cbf --- /dev/null +++ b/modules/customers/src/api/application/snapshot-builders/domain/customer-snapshot.interface.ts @@ -0,0 +1,39 @@ +export interface ICustomerFullSnapshot { + id: string; + company_id: string; + status: string; + reference: string; + + is_company: string; + name: string; + trade_name: string; + tin: string; + + street: string; + street2: string; + city: string; + province: string; + postal_code: string; + country: string; + + email_primary: string; + email_secondary: string; + + phone_primary: string; + phone_secondary: string; + + mobile_primary: string; + mobile_secondary: string; + + fax: string; + website: string; + + legal_record: string; + + default_taxes: string[]; + + language_code: string; + currency_code: string; + + metadata?: Record; +} diff --git a/modules/customers/src/api/application/snapshot-builders/domain/index.ts b/modules/customers/src/api/application/snapshot-builders/domain/index.ts new file mode 100644 index 00000000..ae35536c --- /dev/null +++ b/modules/customers/src/api/application/snapshot-builders/domain/index.ts @@ -0,0 +1,2 @@ +export * from "./customer-snapshot.interface"; +export * from "./customer-snapshot-builder"; diff --git a/modules/customers/src/api/application/snapshot-builders/index.ts b/modules/customers/src/api/application/snapshot-builders/index.ts index 3fa03b33..b7726c46 100644 --- a/modules/customers/src/api/application/snapshot-builders/index.ts +++ b/modules/customers/src/api/application/snapshot-builders/index.ts @@ -1,3 +1,2 @@ -//export * from "./full"; +export * from "./domain"; export * from "./summary"; -//export * from "./report"; diff --git a/modules/customers/src/api/application/snapshot-builders/summary/customer-summary-snapshot-builder.ts b/modules/customers/src/api/application/snapshot-builders/summary/customer-summary-snapshot-builder.ts index 9d78d845..31fa7731 100644 --- a/modules/customers/src/api/application/snapshot-builders/summary/customer-summary-snapshot-builder.ts +++ b/modules/customers/src/api/application/snapshot-builders/summary/customer-summary-snapshot-builder.ts @@ -15,7 +15,7 @@ export class CustomerSummarySnapshotBuilder implements ICustomerSummarySnapshotB return { id: customer.id.toString(), company_id: customer.companyId.toString(), - status: customer.status.toString(), + status: customer.isActive ? "active" : "inactive", reference: maybeToEmptyString(customer.reference, (value) => value.toString()), is_company: String(customer.isCompany), diff --git a/modules/customers/src/api/application/use-cases/create/create-customer.use-case.ts b/modules/customers/src/api/application/use-cases/create/create-customer.use-case.ts index e840a0a6..a4241cc9 100644 --- a/modules/customers/src/api/application/use-cases/create/create-customer.use-case.ts +++ b/modules/customers/src/api/application/use-cases/create/create-customer.use-case.ts @@ -1,12 +1,18 @@ -import { DuplicateEntityError, IPresenterRegistry, ITransactionManager } from "@erp/core/api"; -import { UniqueID } from "@repo/rdx-ddd"; +import { + DuplicateEntityError, + type IPresenterRegistry, + type ITransactionManager, +} from "@erp/core/api"; +import type { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { Transaction } from "sequelize"; -import { CreateCustomerRequestDTO } from "../../../../common"; +import type { Transaction } from "sequelize"; + +import type { CreateCustomerRequestDTO } from "../../../../common"; import { logger } from "../../..//helpers"; -import { CustomerApplicationService } from "../../customer-application.service"; -import { CustomerFullPresenter } from "../../presenters"; +import type { CustomerApplicationService } from "../../customer-application.service"; +import type { CustomerFullSnapshotBuilder } from "../../presenters"; import { CustomerNotExistsInCompanySpecification } from "../../specs"; + import { mapDTOToCreateCustomerProps } from "./map-dto-to-create-customer-props"; type CreateCustomerUseCaseInput = { @@ -26,7 +32,7 @@ export class CreateCustomerUseCase { const presenter = this.presenterRegistry.getPresenter({ resource: "customer", projection: "FULL", - }) as CustomerFullPresenter; + }) as CustomerFullSnapshotBuilder; // 1) Mapear DTO → props de dominio const dtoResult = mapDTOToCreateCustomerProps(dto); diff --git a/modules/customers/src/api/application/use-cases/get-customer-by-id.use-case.ts b/modules/customers/src/api/application/use-cases/get-customer-by-id.use-case.ts new file mode 100644 index 00000000..84e20fe1 --- /dev/null +++ b/modules/customers/src/api/application/use-cases/get-customer-by-id.use-case.ts @@ -0,0 +1,50 @@ +import type { ITransactionManager } from "@erp/core/api"; +import { UniqueID } from "@repo/rdx-ddd"; +import { Result } from "@repo/rdx-utils"; + +import type { ICustomerFinder } from "../services"; +import type { ICustomerFullSnapshotBuilder } from "../snapshot-builders"; + +type GetCustomerUseCaseInput = { + companyId: UniqueID; + customer_id: string; +}; + +export class GetCustomerByIdUseCase { + constructor( + private readonly finder: ICustomerFinder, + private readonly fullSnapshotBuilder: ICustomerFullSnapshotBuilder, + private readonly transactionManager: ITransactionManager + ) {} + + public execute(params: GetCustomerUseCaseInput) { + const { customer_id, companyId } = params; + + const idOrError = UniqueID.create(customer_id); + if (idOrError.isFailure) { + return Result.fail(idOrError.error); + } + + const customerId = idOrError.data; + + return this.transactionManager.complete(async (transaction) => { + try { + const customerResult = await this.finder.findCustomerById( + companyId, + customerId, + transaction + ); + + if (customerResult.isFailure) { + return Result.fail(customerResult.error); + } + + const fullSnapshot = this.fullSnapshotBuilder.toOutput(customerResult.data); + + return Result.ok(fullSnapshot); + } catch (error: unknown) { + return Result.fail(error as Error); + } + }); + } +} diff --git a/modules/customers/src/api/application/use-cases/get-customer.use-case.ts b/modules/customers/src/api/application/use-cases/get-customer.use-case.ts deleted file mode 100644 index a7123fec..00000000 --- a/modules/customers/src/api/application/use-cases/get-customer.use-case.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; -import { UniqueID } from "@repo/rdx-ddd"; -import { Result } from "@repo/rdx-utils"; -import { CustomerApplicationService } from "../../application"; -import { CustomerFullPresenter } from "../presenters"; - -type GetCustomerUseCaseInput = { - companyId: UniqueID; - customer_id: string; -}; - -export class GetCustomerUseCase { - constructor( - private readonly service: CustomerApplicationService, - private readonly transactionManager: ITransactionManager, - private readonly presenterRegistry: IPresenterRegistry - ) {} - - public execute(params: GetCustomerUseCaseInput) { - const { customer_id, companyId } = params; - - const idOrError = UniqueID.create(customer_id); - if (idOrError.isFailure) { - return Result.fail(idOrError.error); - } - - const customerId = idOrError.data; - const presenter = this.presenterRegistry.getPresenter({ - resource: "customer", - projection: "FULL", - }) as CustomerFullPresenter; - - return this.transactionManager.complete(async (transaction) => { - try { - const customerOrError = await this.service.getCustomerByIdInCompany( - companyId, - customerId, - transaction - ); - - if (customerOrError.isFailure) { - return Result.fail(customerOrError.error); - } - - const customer = customerOrError.data; - const dto = presenter.toOutput(customer); - - return Result.ok(dto); - } catch (error: unknown) { - return Result.fail(error as Error); - } - }); - } -} diff --git a/modules/customers/src/api/application/use-cases/index.ts b/modules/customers/src/api/application/use-cases/index.ts index 0c0e823e..e2b69a11 100644 --- a/modules/customers/src/api/application/use-cases/index.ts +++ b/modules/customers/src/api/application/use-cases/index.ts @@ -1,5 +1,5 @@ export * from "./create"; export * from "./delete-customer.use-case"; -export * from "./get-customer.use-case"; +export * from "./get-customer-by-id.use-case"; export * from "./list-customers.use-case"; export * from "./update"; diff --git a/modules/customers/src/api/application/use-cases/update/update-customer.use-case.ts b/modules/customers/src/api/application/use-cases/update/update-customer.use-case.ts index 238a06f2..6bb0d51c 100644 --- a/modules/customers/src/api/application/use-cases/update/update-customer.use-case.ts +++ b/modules/customers/src/api/application/use-cases/update/update-customer.use-case.ts @@ -1,11 +1,13 @@ -import { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; +import type { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; -import { Transaction } from "sequelize"; -import { UpdateCustomerByIdRequestDTO } from "../../../../common/dto"; -import { CustomerPatchProps } from "../../../domain"; -import { CustomerApplicationService } from "../../customer-application.service"; -import { CustomerFullPresenter } from "../../presenters"; +import type { Transaction } from "sequelize"; + +import type { UpdateCustomerByIdRequestDTO } from "../../../../common/dto"; +import type { CustomerPatchProps } from "../../../domain"; +import type { CustomerApplicationService } from "../../customer-application.service"; +import type { CustomerFullSnapshotBuilder } from "../../presenters"; + import { mapDTOToUpdateCustomerPatchProps } from "./map-dto-to-update-customer-props"; type UpdateCustomerUseCaseInput = { @@ -33,7 +35,7 @@ export class UpdateCustomerUseCase { const presenter = this.presenterRegistry.getPresenter({ resource: "customer", projection: "FULL", - }) as CustomerFullPresenter; + }) as CustomerFullSnapshotBuilder; // Mapear DTO → props de dominio const patchPropsResult = mapDTOToUpdateCustomerPatchProps(dto); diff --git a/modules/customers/src/api/infrastructure/di/customers.di.ts b/modules/customers/src/api/infrastructure/di/customers.di.ts index 7fb87006..1a959a46 100644 --- a/modules/customers/src/api/infrastructure/di/customers.di.ts +++ b/modules/customers/src/api/infrastructure/di/customers.di.ts @@ -1,9 +1,11 @@ import { type ModuleParams, buildCatalogs, buildTransactionManager } from "@erp/core/api"; import { + type GetCustomerByIdUseCase, type ListCustomersUseCase, buildCustomerFinder, buildCustomerSnapshotBuilders, + buildGetCustomerByIdUseCase, buildListCustomersUseCase, } from "../../application"; @@ -13,7 +15,7 @@ import { buildCustomerRepository } from "./customer-repositories.di"; export type CustomersInternalDeps = { useCases: { listCustomers: () => ListCustomersUseCase; - //getCustomerById: () => GetCustomerByIdUseCase; + getCustomerById: () => GetCustomerByIdUseCase; //reportCustomer: () => ReportCustomerUseCase; //createCustomer: () => CreateCustomerUseCase; @@ -54,14 +56,14 @@ export function buildCustomersDependencies(params: ModuleParams): CustomersInter transactionManager, }), - /*getCustomerById: () => + getCustomerById: () => buildGetCustomerByIdUseCase({ finder, fullSnapshotBuilder: snapshotBuilders.full, transactionManager, }), - reportCustomer: () => + /*reportCustomer: () => buildReportCustomerUseCase({ finder, fullSnapshotBuilder: snapshotBuilders.full, diff --git a/modules/customers/src/api/infrastructure/express/customers.routes.ts b/modules/customers/src/api/infrastructure/express/customers.routes.ts index cf4f7bb7..ef1528bc 100644 --- a/modules/customers/src/api/infrastructure/express/customers.routes.ts +++ b/modules/customers/src/api/infrastructure/express/customers.routes.ts @@ -7,11 +7,13 @@ import { type ModuleParams, RequestWithAuth, validateRequest } from "@erp/core/a import { type NextFunction, type Request, type Response, Router } from "express"; import { - CustomerListRequestSchema + CustomerListRequestSchema, + GetCustomerByIdRequestSchema } from "../../../common/dto"; import type { CustomersInternalDeps } from "../di"; import { + GetCustomerController, ListCustomersController } from "./controllers"; @@ -51,17 +53,17 @@ export const customersRouter = (params: ModuleParams, deps: CustomersInternalDep } ); - /* router.get( + router.get( "/:customer_id", //checkTabContext, validateRequest(GetCustomerByIdRequestSchema, "params"), (req: Request, res: Response, next: NextFunction) => { - const useCase = deps.useCases.get(); + const useCase = deps.useCases.getCustomerById(); const controller = new GetCustomerController(useCase); return controller.execute(req, res, next); } - ); */ + ); /* router.post( "/", diff --git a/modules/customers/src/api/infrastructure/mappers/summary/sequelize-customer-summary.mapper.ts b/modules/customers/src/api/infrastructure/mappers/summary/sequelize-customer-summary.mapper.ts index da876bb7..35786b1e 100644 --- a/modules/customers/src/api/infrastructure/mappers/summary/sequelize-customer-summary.mapper.ts +++ b/modules/customers/src/api/infrastructure/mappers/summary/sequelize-customer-summary.mapper.ts @@ -190,7 +190,7 @@ export class SequelizeCustomerSummaryMapper return Result.ok({ id: customerId!, companyId: companyId!, - status: status!, + isActive: status!.isActive(), reference: reference!, isCompany: isCompany,