diff --git a/modules/core/src/api/application/presenters/presenter-registry.ts b/modules/core/src/api/application/presenters/presenter-registry.ts index ca304168..b05eefbf 100644 --- a/modules/core/src/api/application/presenters/presenter-registry.ts +++ b/modules/core/src/api/application/presenters/presenter-registry.ts @@ -64,12 +64,12 @@ export class InMemoryPresenterRegistry implements IPresenterRegistry { if (!this.registry.has(exactKey)) { throw new ApplicationError( - `Error. Presenter ${key.resource} ${key.projection} not registred!` + `[InMemoryPresenterRegistry] Presenter not registered: ${key.resource}::${key.projection}` ); } throw new ApplicationError( - `Error. Presenter ${key.resource} / ${key.projection} not registred!` + `[InMemoryPresenterRegistry] Presenter not registered: ${key.resource}::${key.projection}` ); } diff --git a/modules/core/src/api/infrastructure/mappers/mapper-registry.ts b/modules/core/src/api/infrastructure/mappers/mapper-registry.ts index 07208373..fd0585d8 100644 --- a/modules/core/src/api/infrastructure/mappers/mapper-registry.ts +++ b/modules/core/src/api/infrastructure/mappers/mapper-registry.ts @@ -10,14 +10,9 @@ export class InMemoryMapperRegistry implements IMapperRegistry { private _mappers: Map = new Map(); private _normalizeKey(key: MapperDomainKey | MapperQueryKey): MapperKey { - const { resource, query } = key as { - resource: string; - query?: string; - }; - return { - resource, - query: query ?? "DOMAIN", // 👈 valor por defecto + resource: key.resource, + query: "query" in key && key.query ? key.query : "DOMAIN", }; } @@ -37,37 +32,37 @@ export class InMemoryMapperRegistry implements IMapperRegistry { const exactKey = this._buildKey(normalizedKey); if (!this._mappers.has(exactKey)) { - throw new InfrastructureError(`Error. Mapper ${normalizedKey.resource} not registred!`); + throw new InfrastructureError( + `[InMemoryMapperRegistry] Mapper not registered: ${normalizedKey.resource}::${normalizedKey.query}` + ); } return this._mappers.get(exactKey); } + private _registerMapper(key: MapperDomainKey | MapperQueryKey, mapper: T) { + const exactKey = this._buildKey(this._normalizeKey(key)); + this._mappers.set(exactKey, mapper); + } + getDomainMapper(key: MapperDomainKey): T { - const normalizedKey = this._normalizeKey({ - resource: key.resource, + return this._getMapper({ + ...key, query: "DOMAIN", }); - return this._getMapper(normalizedKey); } getQueryMapper(key: MapperQueryKey): T { - const normalizedKey = this._normalizeKey({ - resource: key.resource, - query: "DOMAIN", - }); - return this._getMapper(normalizedKey); + return this._getMapper(key); } registerDomainMapper(key: MapperDomainKey, mapper: T) { - const exactKey = this._buildKey(this._normalizeKey(key)); - this._mappers.set(exactKey, mapper); + this._registerMapper(key, mapper); return this; } registerQueryMapper(key: MapperQueryKey, mapper: T) { - const exactKey = this._buildKey(this._normalizeKey(key)); - this._mappers.set(exactKey, mapper); + this._registerMapper(key, mapper); return this; } diff --git a/modules/customer-invoices/src/api/application/presenters/domain/customer-invoice.full.presenter.ts b/modules/customer-invoices/src/api/application/presenters/domain/customer-invoice.full.presenter.ts index 97d4d673..2b6cca82 100644 --- a/modules/customer-invoices/src/api/application/presenters/domain/customer-invoice.full.presenter.ts +++ b/modules/customer-invoices/src/api/application/presenters/domain/customer-invoice.full.presenter.ts @@ -4,7 +4,10 @@ import { GetCustomerInvoiceByIdResponseDTO } from "../../../../common/dto"; import { CustomerInvoice } from "../../../domain"; import { CustomerInvoiceItemsFullPresenter } from "./customer-invoice-items.full.presenter"; -export class CustomerInvoiceFullPresenter extends Presenter { +export class CustomerInvoiceFullPresenter extends Presenter< + CustomerInvoice, + GetCustomerInvoiceByIdResponseDTO +> { toOutput(invoice: CustomerInvoice): GetCustomerInvoiceByIdResponseDTO { const itemsPresenter = this.presenterRegistry.getPresenter({ resource: "customer-invoice-items", diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/create-customer-invoice.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/create-customer-invoice.controller.ts index 588e9450..4c111d2b 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/controllers/create-customer-invoice.controller.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/create-customer-invoice.controller.ts @@ -11,7 +11,10 @@ export class CreateCustomerInvoiceController extends ExpressController { } protected async executeImpl() { - const companyId = this.getTenantId()!; // garantizado por tenantGuard + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } const dto = this.req.body as CreateCustomerInvoiceRequestDTO; const result = await this.useCase.execute({ dto, companyId }); diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/get-customer-invoice.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/get-customer-invoice.controller.ts index ada27adb..06f3dd21 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/controllers/get-customer-invoice.controller.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/get-customer-invoice.controller.ts @@ -9,7 +9,10 @@ export class GetCustomerInvoiceController extends ExpressController { } protected async executeImpl() { - const companyId = this.getTenantId()!; // garantizado por tenantGuard + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } const { invoice_id } = this.req.params; const result = await this.useCase.execute({ invoice_id, companyId }); diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/list-customer-invoices.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/list-customer-invoices.controller.ts index d8849af4..b8f95a55 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/controllers/list-customer-invoices.controller.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/list-customer-invoices.controller.ts @@ -9,7 +9,7 @@ export class ListCustomerInvoicesController extends ExpressController { this.useGuards(authGuard(), tenantGuard(), forbidQueryFieldGuard("companyId")); } - private _xxxx() { + private getCriteriaWithDefaultOrder() { if (this.criteria.hasOrder()) { return this.criteria; } @@ -19,9 +19,12 @@ export class ListCustomerInvoicesController extends ExpressController { } protected async executeImpl() { - const companyId = this.getTenantId()!; // garantizado por tenantGuard + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } - const criteria = this._xxxx(); + const criteria = this.getCriteriaWithDefaultOrder(); const result = await this.useCase.execute({ criteria, companyId }); return result.match( diff --git a/modules/customer-invoices/src/api/infrastructure/express/controllers/report-customer-invoice.controller.ts b/modules/customer-invoices/src/api/infrastructure/express/controllers/report-customer-invoice.controller.ts index 37d9dcda..ecbaee63 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/controllers/report-customer-invoice.controller.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/controllers/report-customer-invoice.controller.ts @@ -9,7 +9,10 @@ export class ReportCustomerInvoiceController extends ExpressController { } protected async executeImpl() { - const companyId = this.getTenantId()!; // garantizado por tenantGuard + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } const { invoice_id } = this.req.params; const result = await this.useCase.execute({ invoice_id, companyId }); diff --git a/modules/customers/src/api/application/create-customer/presenter/create-customers.presenter.ts b/modules/customers/src/api/application/create-customer/presenter/create-customers.presenter.ts deleted file mode 100644 index b719588e..00000000 --- a/modules/customers/src/api/application/create-customer/presenter/create-customers.presenter.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { toEmptyString } from "@repo/rdx-ddd"; -import { CustomerCreationResponseDTO } from "../../../../common"; -import { Customer } from "../../../domain"; - -export class CreateCustomersPresenter { - public toDTO(customer: Customer): CustomerCreationResponseDTO { - const address = customer.address.toPrimitive(); - - return { - id: customer.id.toPrimitive(), - company_id: customer.companyId.toPrimitive(), - - reference: toEmptyString(customer.reference, (value) => value.toPrimitive()), - - is_company: String(customer.isCompany), - name: customer.name.toPrimitive(), - - trade_name: toEmptyString(customer.tradeName, (value) => value.toPrimitive()), - - tin: toEmptyString(customer.tin, (value) => value.toPrimitive()), - - street: toEmptyString(address.street, (value) => value.toPrimitive()), - street2: toEmptyString(address.street2, (value) => value.toPrimitive()), - city: toEmptyString(address.city, (value) => value.toPrimitive()), - province: toEmptyString(address.province, (value) => value.toPrimitive()), - postal_code: toEmptyString(address.postalCode, (value) => value.toPrimitive()), - country: toEmptyString(address.country, (value) => value.toPrimitive()), - - email: toEmptyString(customer.email, (value) => value.toPrimitive()), - phone: toEmptyString(customer.phone, (value) => value.toPrimitive()), - fax: toEmptyString(customer.fax, (value) => value.toPrimitive()), - website: toEmptyString(customer.website, (value) => value.toPrimitive()), - - legal_record: toEmptyString(customer.legalRecord, (value) => value.toPrimitive()), - - default_taxes: customer.defaultTaxes.getAll().join(", "), - - status: customer.isActive ? "active" : "inactive", - language_code: customer.languageCode.toPrimitive(), - currency_code: customer.currencyCode.toPrimitive(), - - metadata: { - entity: "customer", - }, - }; - } -} diff --git a/modules/customers/src/api/application/create-customer/presenter/index.ts b/modules/customers/src/api/application/create-customer/presenter/index.ts deleted file mode 100644 index e63b640f..00000000 --- a/modules/customers/src/api/application/create-customer/presenter/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./create-customers.presenter"; diff --git a/modules/customers/src/api/application/delete-customer/index.ts b/modules/customers/src/api/application/delete-customer/index.ts deleted file mode 100644 index 3280b04b..00000000 --- a/modules/customers/src/api/application/delete-customer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./delete-customer.use-case"; diff --git a/modules/customers/src/api/application/get-customer/index.ts b/modules/customers/src/api/application/get-customer/index.ts deleted file mode 100644 index 41efed3f..00000000 --- a/modules/customers/src/api/application/get-customer/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./presenter"; -export * from "./get-customer.use-case"; diff --git a/modules/customers/src/api/application/get-customer/presenter/InvoiceItem.presenter.ts.bak b/modules/customers/src/api/application/get-customer/presenter/InvoiceItem.presenter.ts.bak deleted file mode 100644 index 3a6e2c6e..00000000 --- a/modules/customers/src/api/application/get-customer/presenter/InvoiceItem.presenter.ts.bak +++ /dev/null @@ -1,16 +0,0 @@ -import { CustomerItem } from "#/server/domain"; -import { IInvoicingContext } from "#/server/intrastructure"; -import { Collection } from "@rdx/core"; - -export const customerItemPresenter = (items: Collection, context: IInvoicingContext) => - items.totalCount > 0 - ? items.items.map((item: CustomerItem) => ({ - description: item.description.toString(), - quantity: item.quantity.toString(), - unit_measure: "", - unit_price: item.unitPrice.toPrimitive() as IMoney_Response_DTO, - subtotal: item.calculateSubtotal().toPrimitive() as IMoney_Response_DTO, - tax_amount: item.calculateTaxAmount().toPrimitive() as IMoney_Response_DTO, - total: item.calculateTotal().toPrimitive() as IMoney_Response_DTO, - })) - : []; diff --git a/modules/customers/src/api/application/get-customer/presenter/InvoiceParticipant.presenter.ts.bak b/modules/customers/src/api/application/get-customer/presenter/InvoiceParticipant.presenter.ts.bak deleted file mode 100644 index d10d3001..00000000 --- a/modules/customers/src/api/application/get-customer/presenter/InvoiceParticipant.presenter.ts.bak +++ /dev/null @@ -1,26 +0,0 @@ -import { ICustomerParticipant } from "@/contexts/invoicing/domain"; -import { IInvoicingContext } from "@/contexts/invoicing/intrastructure/InvoicingContext"; -import { ICreateCustomer_Participant_Response_DTO } from "@shared/contexts"; -import { CustomerParticipantAddressPresenter } from "./CustomerParticipantAddress.presenter"; - -export const CustomerParticipantPresenter = async ( - participant: ICustomerParticipant, - context: IInvoicingContext, -): Promise => { - return { - id: participant.id.toString(), - tin: participant.tin.toString(), - first_name: participant.firstName.toString(), - last_name: participant.lastName.toString(), - company_name: participant.companyName.toString(), - - billing_address: await CustomerParticipantAddressPresenter( - participant.billingAddress!, - context, - ), - shipping_address: await CustomerParticipantAddressPresenter( - participant.shippingAddress!, - context, - ), - }; -}; diff --git a/modules/customers/src/api/application/get-customer/presenter/InvoiceParticipantAddress.presenter.ts.bak b/modules/customers/src/api/application/get-customer/presenter/InvoiceParticipantAddress.presenter.ts.bak deleted file mode 100644 index 6266fda0..00000000 --- a/modules/customers/src/api/application/get-customer/presenter/InvoiceParticipantAddress.presenter.ts.bak +++ /dev/null @@ -1,19 +0,0 @@ -import { CustomerParticipantAddress } from "@/contexts/invoicing/domain"; -import { IInvoicingContext } from "@/contexts/invoicing/intrastructure/InvoicingContext"; -import { ICreateCustomer_AddressParticipant_Response_DTO } from "@shared/contexts"; - -export const CustomerParticipantAddressPresenter = async ( - address: CustomerParticipantAddress, - context: IInvoicingContext, -): Promise => { - return { - id: address.id.toString(), - street: address.street.toString(), - city: address.city.toString(), - postal_code: address.postalCode.toString(), - province: address.province.toString(), - country: address.country.toString(), - email: address.email.toString(), - phone: address.phone.toString(), - }; -}; diff --git a/modules/customers/src/api/application/get-customer/presenter/index.ts b/modules/customers/src/api/application/get-customer/presenter/index.ts deleted file mode 100644 index f713440d..00000000 --- a/modules/customers/src/api/application/get-customer/presenter/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./get-customer.presenter"; diff --git a/modules/customers/src/api/application/index.ts b/modules/customers/src/api/application/index.ts index 0858943b..8643bc2a 100644 --- a/modules/customers/src/api/application/index.ts +++ b/modules/customers/src/api/application/index.ts @@ -1,5 +1,2 @@ -export * from "./create-customer"; -export * from "./delete-customer"; -export * from "./get-customer"; -export * from "./list-customers"; -export * from "./update-customer"; +export * from "./presenters"; +export * from "./use-cases"; diff --git a/modules/customers/src/api/application/list-customers/index.ts b/modules/customers/src/api/application/list-customers/index.ts deleted file mode 100644 index 7c451c94..00000000 --- a/modules/customers/src/api/application/list-customers/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./presenter"; -export * from "./list-customers.use-case"; diff --git a/modules/customers/src/api/application/list-customers/presenter/list-customers.presenter.ts b/modules/customers/src/api/application/list-customers/presenter/list-customers.presenter.ts deleted file mode 100644 index 17d92f6f..00000000 --- a/modules/customers/src/api/application/list-customers/presenter/list-customers.presenter.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Criteria } from "@repo/rdx-criteria/server"; -import { toEmptyString } from "@repo/rdx-ddd"; -import { Collection } from "@repo/rdx-utils"; -import { CustomerListResponsetDTO } from "../../../../common/dto"; -import { Customer } from "../../../domain"; - -export class ListCustomersPresenter { - toDTO(customers: Collection, criteria: Criteria): CustomerListResponsetDTO { - const items: CustomerListResponsetDTO["items"] = customers.map((customer) => { - const address = customer.address.toPrimitive(); - - return { - id: customer.id.toPrimitive(), - company_id: customer.companyId.toPrimitive(), - - reference: toEmptyString(customer.reference, (value) => value.toPrimitive()), - - is_company: String(customer.isCompany), - name: customer.name.toPrimitive(), - - trade_name: toEmptyString(customer.tradeName, (value) => value.toPrimitive()), - - tin: toEmptyString(customer.tin, (value) => value.toPrimitive()), - - street: toEmptyString(address.street, (value) => value.toPrimitive()), - street2: toEmptyString(address.street2, (value) => value.toPrimitive()), - city: toEmptyString(address.city, (value) => value.toPrimitive()), - province: toEmptyString(address.province, (value) => value.toPrimitive()), - postal_code: toEmptyString(address.postalCode, (value) => value.toPrimitive()), - country: toEmptyString(address.country, (value) => value.toPrimitive()), - - email: toEmptyString(customer.email, (value) => value.toPrimitive()), - phone: toEmptyString(customer.phone, (value) => value.toPrimitive()), - fax: toEmptyString(customer.fax, (value) => value.toPrimitive()), - website: toEmptyString(customer.website, (value) => value.toPrimitive()), - - legal_record: toEmptyString(customer.legalRecord, (value) => value.toPrimitive()), - - default_taxes: customer.defaultTaxes.getAll().join(", "), - - status: customer.isActive ? "active" : "inactive", - language_code: customer.languageCode.toPrimitive(), - currency_code: customer.currencyCode.toPrimitive(), - - metadata: { - entity: "customer", - //created_at: customer.createdAt.toPrimitive(), - //updated_at: customer.updatedAt.toPrimitive() - }, - }; - }); - - const totalItems = customers.total(); - - return { - page: criteria.pageNumber, - per_page: criteria.pageSize, - total_pages: Math.ceil(totalItems / criteria.pageSize), - total_items: totalItems, - items: items, - metadata: { - entity: "customers", - criteria: criteria.toJSON(), - //links: { - // self: `/api/customers?page=${criteria.pageNumber}&per_page=${criteria.pageSize}`, - // first: `/api/customers?page=1&per_page=${criteria.pageSize}`, - // last: `/api/customers?page=${Math.ceil(totalItems / criteria.pageSize)}&per_page=${criteria.pageSize}`, - //}, - }, - }; - } -} diff --git a/modules/customers/src/api/application/get-customer/presenter/get-customer.presenter.ts b/modules/customers/src/api/application/presenters/domain/customer.full.presenter.ts similarity index 90% rename from modules/customers/src/api/application/get-customer/presenter/get-customer.presenter.ts rename to modules/customers/src/api/application/presenters/domain/customer.full.presenter.ts index ae8523d4..f9b929a1 100644 --- a/modules/customers/src/api/application/get-customer/presenter/get-customer.presenter.ts +++ b/modules/customers/src/api/application/presenters/domain/customer.full.presenter.ts @@ -1,9 +1,10 @@ +import { Presenter } from "@erp/core/api"; import { toEmptyString } from "@repo/rdx-ddd"; import { GetCustomerByIdResponseDTO } from "../../../../common/dto"; import { Customer } from "../../../domain"; -export class GetCustomerPresenter { - toDTO(customer: Customer): GetCustomerByIdResponseDTO { +export class CustomerFullPresenter extends Presenter { + toOutput(customer: Customer): GetCustomerByIdResponseDTO { const address = customer.address.toPrimitive(); return { diff --git a/modules/customers/src/api/application/presenters/domain/index.ts b/modules/customers/src/api/application/presenters/domain/index.ts new file mode 100644 index 00000000..f2d46ce5 --- /dev/null +++ b/modules/customers/src/api/application/presenters/domain/index.ts @@ -0,0 +1 @@ +export * from "./customer.full.presenter"; diff --git a/modules/customers/src/api/application/presenters/index.ts b/modules/customers/src/api/application/presenters/index.ts new file mode 100644 index 00000000..9e03d7a9 --- /dev/null +++ b/modules/customers/src/api/application/presenters/index.ts @@ -0,0 +1,2 @@ +export * from "./domain"; +export * from "./queries"; diff --git a/modules/customers/src/api/application/list-customers/presenter/index.ts b/modules/customers/src/api/application/presenters/queries/index.ts similarity index 100% rename from modules/customers/src/api/application/list-customers/presenter/index.ts rename to modules/customers/src/api/application/presenters/queries/index.ts diff --git a/modules/customers/src/api/application/update-customer/presenter/update-customer.presenter.ts b/modules/customers/src/api/application/presenters/queries/list-customers.presenter.ts similarity index 54% rename from modules/customers/src/api/application/update-customer/presenter/update-customer.presenter.ts rename to modules/customers/src/api/application/presenters/queries/list-customers.presenter.ts index f72b74d3..327b3e05 100644 --- a/modules/customers/src/api/application/update-customer/presenter/update-customer.presenter.ts +++ b/modules/customers/src/api/application/presenters/queries/list-customers.presenter.ts @@ -1,9 +1,12 @@ +import { Presenter } from "@erp/core/api"; +import { CustomerListDTO } from "@erp/customer-invoices/api/infrastructure"; +import { Criteria } from "@repo/rdx-criteria/server"; import { toEmptyString } from "@repo/rdx-ddd"; -import { UpdateCustomerByIdResponseDTO } from "../../../../common/dto"; -import { Customer } from "../../../domain"; +import { Collection } from "@repo/rdx-utils"; +import { CustomerListResponsetDTO } from "../../../../common/dto"; -export class UpdateCustomerPresenter { - toDTO(customer: Customer): UpdateCustomerByIdResponseDTO { +export class ListCustomersPresenter extends Presenter { + protected _mapCustomer(customer: CustomerListDTO) { const address = customer.address.toPrimitive(); return { @@ -31,20 +34,42 @@ export class UpdateCustomerPresenter { fax: toEmptyString(customer.fax, (value) => value.toPrimitive()), website: toEmptyString(customer.website, (value) => value.toPrimitive()), - legal_record: toEmptyString(customer.legalRecord, (value) => value.toPrimitive()), - - default_taxes: customer.defaultTaxes.getAll().join(", "), - - status: customer.isActive ? "active" : "inactive", + status: customer.status ? "active" : "inactive", language_code: customer.languageCode.toPrimitive(), currency_code: customer.currencyCode.toPrimitive(), metadata: { entity: "customer", - //id: customer.id.toPrimitive(), //created_at: customer.createdAt.toPrimitive(), //updated_at: customer.updatedAt.toPrimitive() }, }; } + + toOutput(params: { + customers: Collection; + criteria: Criteria; + }): CustomerListResponsetDTO { + const { customers, criteria } = params; + + const items = customers.map((customer) => this._mapCustomer(customer)); + const totalItems = customers.total(); + + return { + page: criteria.pageNumber, + per_page: criteria.pageSize, + total_pages: Math.ceil(totalItems / criteria.pageSize), + total_items: totalItems, + items: items, + metadata: { + entity: "customers", + criteria: criteria.toJSON(), + //links: { + // self: `/api/customer-invoices?page=${criteria.pageNumber}&per_page=${criteria.pageSize}`, + // first: `/api/customer-invoices?page=1&per_page=${criteria.pageSize}`, + // last: `/api/customer-invoices?page=${Math.ceil(totalItems / criteria.pageSize)}&per_page=${criteria.pageSize}`, + //}, + }, + }; + } } diff --git a/modules/customers/src/api/application/update-customer/presenter/index.ts b/modules/customers/src/api/application/update-customer/presenter/index.ts deleted file mode 100644 index 7ac3153a..00000000 --- a/modules/customers/src/api/application/update-customer/presenter/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./update-customer.presenter"; diff --git a/modules/customers/src/api/application/create-customer/create-customer.use-case.ts b/modules/customers/src/api/application/use-cases/create/create-customer.use-case.ts similarity index 100% rename from modules/customers/src/api/application/create-customer/create-customer.use-case.ts rename to modules/customers/src/api/application/use-cases/create/create-customer.use-case.ts diff --git a/modules/customers/src/api/application/create-customer/index.ts b/modules/customers/src/api/application/use-cases/create/index.ts similarity index 60% rename from modules/customers/src/api/application/create-customer/index.ts rename to modules/customers/src/api/application/use-cases/create/index.ts index d31522a4..507465dd 100644 --- a/modules/customers/src/api/application/create-customer/index.ts +++ b/modules/customers/src/api/application/use-cases/create/index.ts @@ -1,2 +1 @@ -export * from "./presenter"; export * from "./create-customer.use-case"; diff --git a/modules/customers/src/api/application/create-customer/map-dto-to-create-customer-props.ts b/modules/customers/src/api/application/use-cases/create/map-dto-to-create-customer-props.ts similarity index 97% rename from modules/customers/src/api/application/create-customer/map-dto-to-create-customer-props.ts rename to modules/customers/src/api/application/use-cases/create/map-dto-to-create-customer-props.ts index f9d2252c..76e81e61 100644 --- a/modules/customers/src/api/application/create-customer/map-dto-to-create-customer-props.ts +++ b/modules/customers/src/api/application/use-cases/create/map-dto-to-create-customer-props.ts @@ -24,8 +24,8 @@ import { maybeFromNullableVO, } from "@repo/rdx-ddd"; import { Collection, Result, isNullishOrEmpty } from "@repo/rdx-utils"; -import { CreateCustomerRequestDTO } from "../../../common/dto"; -import { CustomerProps, CustomerStatus } from "../../domain"; +import { CreateCustomerRequestDTO } from "../../../../common"; +import { CustomerProps, CustomerStatus } from "../../../domain"; /** * Convierte el DTO a las props validadas (CustomerProps). diff --git a/modules/customers/src/api/application/delete-customer/delete-customer.use-case.ts b/modules/customers/src/api/application/use-cases/delete-customer.use-case.ts similarity index 91% rename from modules/customers/src/api/application/delete-customer/delete-customer.use-case.ts rename to modules/customers/src/api/application/use-cases/delete-customer.use-case.ts index b3f44954..2853d586 100644 --- a/modules/customers/src/api/application/delete-customer/delete-customer.use-case.ts +++ b/modules/customers/src/api/application/use-cases/delete-customer.use-case.ts @@ -40,9 +40,7 @@ export class DeleteCustomerUseCase { const customerExists = existsCheck.data; if (!customerExists) { - return Result.fail( - new EntityNotFoundError("Customer", "id", customerId.toObjectString()) - ); + return Result.fail(new EntityNotFoundError("Customer", "id", customerId.toString())); } return await this.service.deleteCustomerByIdInCompany(customerId, companyId, transaction); diff --git a/modules/customers/src/api/application/get-customer/get-customer.use-case.ts b/modules/customers/src/api/application/use-cases/get-customer.use-case.ts similarity index 70% rename from modules/customers/src/api/application/get-customer/get-customer.use-case.ts rename to modules/customers/src/api/application/use-cases/get-customer.use-case.ts index 99e94924..31227d2d 100644 --- a/modules/customers/src/api/application/get-customer/get-customer.use-case.ts +++ b/modules/customers/src/api/application/use-cases/get-customer.use-case.ts @@ -1,8 +1,8 @@ -import { ITransactionManager } from "@erp/core/api"; +import { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; import { CustomerService } from "../../domain"; -import { GetCustomerAssembler } from "./assembler"; +import { CustomerFullPresenter } from "../presenters"; type GetCustomerUseCaseInput = { companyId: UniqueID; @@ -13,19 +13,22 @@ export class GetCustomerUseCase { constructor( private readonly service: CustomerService, private readonly transactionManager: ITransactionManager, - private readonly assembler: GetCustomerAssembler + 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 { @@ -39,8 +42,10 @@ export class GetCustomerUseCase { return Result.fail(customerOrError.error); } - const getDTO = this.assembler.toDTO(customerOrError.data); - return Result.ok(getDTO); + 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 new file mode 100644 index 00000000..0c0e823e --- /dev/null +++ b/modules/customers/src/api/application/use-cases/index.ts @@ -0,0 +1,5 @@ +export * from "./create"; +export * from "./delete-customer.use-case"; +export * from "./get-customer.use-case"; +export * from "./list-customers.use-case"; +export * from "./update"; diff --git a/modules/customers/src/api/application/list-customers/list-customers.use-case.ts b/modules/customers/src/api/application/use-cases/list-customers.use-case.ts similarity index 63% rename from modules/customers/src/api/application/list-customers/list-customers.use-case.ts rename to modules/customers/src/api/application/use-cases/list-customers.use-case.ts index 2967eda4..09e03377 100644 --- a/modules/customers/src/api/application/list-customers/list-customers.use-case.ts +++ b/modules/customers/src/api/application/use-cases/list-customers.use-case.ts @@ -1,11 +1,11 @@ -import { ITransactionManager } from "@erp/core/api"; +import { IPresenterRegistry, ITransactionManager } from "@erp/core/api"; import { Criteria } from "@repo/rdx-criteria/server"; import { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; import { Transaction } from "sequelize"; import { CustomerListResponsetDTO } from "../../../common/dto"; import { CustomerService } from "../../domain"; -import { ListCustomersAssembler } from "./assembler"; +import { ListCustomersPresenter } from "../presenters"; type ListCustomersUseCaseInput = { companyId: UniqueID; @@ -14,19 +14,23 @@ type ListCustomersUseCaseInput = { export class ListCustomersUseCase { constructor( - private readonly customerService: CustomerService, + private readonly service: CustomerService, private readonly transactionManager: ITransactionManager, - private readonly assembler: ListCustomersAssembler + private readonly presenterRegistry: IPresenterRegistry ) {} public execute( params: ListCustomersUseCaseInput ): Promise> { const { criteria, companyId } = params; + const presenter = this.presenterRegistry.getPresenter({ + resource: "customer", + projection: "LIST", + }) as ListCustomersPresenter; return this.transactionManager.complete(async (transaction: Transaction) => { try { - const result = await this.customerService.findCustomerByCriteriaInCompany( + const result = await this.service.findCustomerByCriteriaInCompany( companyId, criteria, transaction @@ -36,7 +40,12 @@ export class ListCustomersUseCase { return Result.fail(result.error); } - const dto = this.assembler.toDTO(result.data, criteria); + const customers = result.data; + const dto = presenter.toOutput({ + customers, + criteria, + }); + return Result.ok(dto); } catch (error: unknown) { return Result.fail(error as Error); diff --git a/modules/customers/src/api/application/update-customer/index.ts b/modules/customers/src/api/application/use-cases/update/index.ts similarity index 60% rename from modules/customers/src/api/application/update-customer/index.ts rename to modules/customers/src/api/application/use-cases/update/index.ts index afa93ec4..db3e8d69 100644 --- a/modules/customers/src/api/application/update-customer/index.ts +++ b/modules/customers/src/api/application/use-cases/update/index.ts @@ -1,2 +1 @@ -export * from "./presenter"; export * from "./update-customer.use-case"; diff --git a/modules/customers/src/api/application/update-customer/map-dto-to-update-customer-props.ts b/modules/customers/src/api/application/use-cases/update/map-dto-to-update-customer-props.ts similarity index 100% rename from modules/customers/src/api/application/update-customer/map-dto-to-update-customer-props.ts rename to modules/customers/src/api/application/use-cases/update/map-dto-to-update-customer-props.ts diff --git a/modules/customers/src/api/application/update-customer/update-customer.use-case.ts b/modules/customers/src/api/application/use-cases/update/update-customer.use-case.ts similarity index 100% rename from modules/customers/src/api/application/update-customer/update-customer.use-case.ts rename to modules/customers/src/api/application/use-cases/update/update-customer.use-case.ts diff --git a/modules/customers/src/api/domain/services/customer.service.ts b/modules/customers/src/api/domain/services/customer.service.ts index 092c9fba..c6fdec15 100644 --- a/modules/customers/src/api/domain/services/customer.service.ts +++ b/modules/customers/src/api/domain/services/customer.service.ts @@ -1,6 +1,7 @@ import { Criteria } from "@repo/rdx-criteria/server"; import { UniqueID } from "@repo/rdx-ddd"; import { Collection, Result } from "@repo/rdx-utils"; +import { CustomerListDTO } from "../../infrastructure"; import { Customer, CustomerPatchProps, CustomerProps } from "../aggregates"; import { ICustomerRepository } from "../repositories"; @@ -64,7 +65,7 @@ export class CustomerService { companyId: UniqueID, criteria: Criteria, transaction?: any - ): Promise, Error>> { + ): Promise, Error>> { return this.repository.findByCriteriaInCompany(companyId, criteria, transaction); } diff --git a/modules/customers/src/api/infrastructure/dependencies.ts b/modules/customers/src/api/infrastructure/dependencies.ts index c29bd915..8a0f5037 100644 --- a/modules/customers/src/api/infrastructure/dependencies.ts +++ b/modules/customers/src/api/infrastructure/dependencies.ts @@ -4,16 +4,14 @@ import { InMemoryPresenterRegistry, SequelizeTransactionManager, } from "@erp/core/api"; - import { - CreateCustomerUseCase, - DeleteCustomerUseCase, - GetCustomerUseCase, + CustomerFullPresenter, + ListCustomersPresenter, ListCustomersUseCase, - UpdateCustomerUseCase, } from "../application"; +import { GetCustomerUseCase } from "../application/use-cases/get-customer.use-case"; import { CustomerService } from "../domain"; -import { CustomerDomainMapper } from "./mappers"; +import { CustomerDomainMapper, CustomerListMapper } from "./mappers"; import { CustomerRepository } from "./sequelize"; export type CustomerDeps = { @@ -25,9 +23,9 @@ export type CustomerDeps = { build: { list: () => ListCustomersUseCase; get: () => GetCustomerUseCase; - create: () => CreateCustomerUseCase; + /*create: () => CreateCustomerUseCase; update: () => UpdateCustomerUseCase; - delete: () => DeleteCustomerUseCase; + delete: () => DeleteCustomerUseCase;*/ }; }; @@ -37,10 +35,12 @@ export function buildCustomerDependencies(params: ModuleParams): CustomerDeps { // Mapper Registry const mapperRegistry = new InMemoryMapperRegistry(); - mapperRegistry.registerDomainMapper({ resource: "customer" }, new CustomerDomainMapper()); + mapperRegistry + .registerDomainMapper({ resource: "customer" }, new CustomerDomainMapper()) + .registerQueryMapper({ resource: "customer", query: "LIST" }, new CustomerListMapper()); // Repository & Services - const repo = new CustomerRepository({ mapperRegistry: _mapperRegistry, database }); + const repo = new CustomerRepository({ mapperRegistry, database }); const service = new CustomerService(repo); // Presenter Registry @@ -48,11 +48,11 @@ export function buildCustomerDependencies(params: ModuleParams): CustomerDeps { presenterRegistry.registerPresenters([ { key: { resource: "customer", projection: "FULL" }, - presenter: new ListCustomersAssembler(), + presenter: new CustomerFullPresenter(presenterRegistry), }, { key: { resource: "customer", projection: "LIST" }, - presenter: new GetCustomerAssembler(), + presenter: new ListCustomersPresenter(presenterRegistry), }, ]); @@ -63,8 +63,8 @@ export function buildCustomerDependencies(params: ModuleParams): CustomerDeps { presenterRegistry, service, build: { - /*list: () => new ListCustomersUseCase(_service!, transactionManager!, presenterRegistry!), - get: () => new GetCustomerUseCase(_service!, transactionManager!, presenterRegistry!), + list: () => new ListCustomersUseCase(service, transactionManager, presenterRegistry), + /*get: () => new GetCustomerUseCase(_service!, transactionManager!, presenterRegistry!), create: () => new CreateCustomerUseCase(_service!, transactionManager!, presenterRegistry!), update: () => new UpdateCustomerUseCase(_service!, transactionManager!, presenterRegistry!), delete: () => new DeleteCustomerUseCase(_service!, transactionManager!),*/ diff --git a/modules/customers/src/api/infrastructure/express/controllers/create-customer.controller.ts b/modules/customers/src/api/infrastructure/express/controllers/create-customer.controller.ts index ccd293de..2103368c 100644 --- a/modules/customers/src/api/infrastructure/express/controllers/create-customer.controller.ts +++ b/modules/customers/src/api/infrastructure/express/controllers/create-customer.controller.ts @@ -10,7 +10,10 @@ export class CreateCustomerController extends ExpressController { } protected async executeImpl() { - const companyId = this.getTenantId()!; // garantizado por tenantGuard + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } const dto = this.req.body as CreateCustomerRequestDTO; const result = await this.useCase.execute({ dto, companyId }); diff --git a/modules/customers/src/api/infrastructure/express/controllers/delete-customer.controller.ts b/modules/customers/src/api/infrastructure/express/controllers/delete-customer.controller.ts index e246adc3..20dc8215 100644 --- a/modules/customers/src/api/infrastructure/express/controllers/delete-customer.controller.ts +++ b/modules/customers/src/api/infrastructure/express/controllers/delete-customer.controller.ts @@ -9,7 +9,10 @@ export class DeleteCustomerController extends ExpressController { } async executeImpl(): Promise { - const companyId = this.getTenantId()!; // garantizado por tenantGuard + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } const { customer_id } = this.req.params; const result = await this.useCase.execute({ customer_id, companyId }); diff --git a/modules/customers/src/api/infrastructure/express/controllers/get-customer.controller.ts b/modules/customers/src/api/infrastructure/express/controllers/get-customer.controller.ts index ba923181..aaf367b0 100644 --- a/modules/customers/src/api/infrastructure/express/controllers/get-customer.controller.ts +++ b/modules/customers/src/api/infrastructure/express/controllers/get-customer.controller.ts @@ -9,7 +9,10 @@ export class GetCustomerController extends ExpressController { } protected async executeImpl() { - const companyId = this.getTenantId()!; // garantizado por tenantGuard + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } const { customer_id } = this.req.params; const result = await this.useCase.execute({ customer_id, companyId }); diff --git a/modules/customers/src/api/infrastructure/express/controllers/list-customers.controller.ts b/modules/customers/src/api/infrastructure/express/controllers/list-customers.controller.ts index 44ce2a48..20e9c4da 100644 --- a/modules/customers/src/api/infrastructure/express/controllers/list-customers.controller.ts +++ b/modules/customers/src/api/infrastructure/express/controllers/list-customers.controller.ts @@ -1,4 +1,5 @@ import { ExpressController, authGuard, forbidQueryFieldGuard, tenantGuard } from "@erp/core/api"; +import { Criteria } from "@repo/rdx-criteria/server"; import { ListCustomersUseCase } from "../../../application"; export class ListCustomersController extends ExpressController { @@ -8,9 +9,23 @@ export class ListCustomersController extends ExpressController { this.useGuards(authGuard(), tenantGuard(), forbidQueryFieldGuard("companyId")); } + private getCriteriaWithDefaultOrder() { + if (this.criteria.hasOrder()) { + return this.criteria; + } + + const { filters, pageSize, pageNumber } = this.criteria.toPrimitives(); + return Criteria.fromPrimitives(filters, "name", "ASC", pageSize, pageNumber); + } + protected async executeImpl() { - const companyId = this.getTenantId()!; // garantizado por tenantGuard - const result = await this.listCustomers.execute({ criteria: this.criteria, companyId }); + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } + + const criteria = this.getCriteriaWithDefaultOrder(); + const result = await this.listCustomers.execute({ criteria, companyId }); return result.match( (data) => diff --git a/modules/customers/src/api/infrastructure/express/controllers/update-customer.controller.ts b/modules/customers/src/api/infrastructure/express/controllers/update-customer.controller.ts index 1626cb2c..f3d69400 100644 --- a/modules/customers/src/api/infrastructure/express/controllers/update-customer.controller.ts +++ b/modules/customers/src/api/infrastructure/express/controllers/update-customer.controller.ts @@ -10,7 +10,10 @@ export class UpdateCustomerController extends ExpressController { } protected async executeImpl() { - const companyId = this.getTenantId()!; // garantizado por tenantGuard + const companyId = this.getTenantId(); + if (!companyId) { + return this.forbiddenError("Tenant ID not found"); + } const { customer_id } = this.req.params; const dto = this.req.body as UpdateCustomerRequestDTO; diff --git a/modules/customers/src/api/infrastructure/express/customers.routes.ts b/modules/customers/src/api/infrastructure/express/customers.routes.ts index 4394cf88..aba37506 100644 --- a/modules/customers/src/api/infrastructure/express/customers.routes.ts +++ b/modules/customers/src/api/infrastructure/express/customers.routes.ts @@ -2,22 +2,9 @@ import { RequestWithAuth, enforceTenant, enforceUser, mockUser } from "@erp/auth import { ILogger, ModuleParams, validateRequest } from "@erp/core/api"; import { Application, NextFunction, Request, Response, Router } from "express"; import { Sequelize } from "sequelize"; -import { - CreateCustomerRequestSchema, - CustomerListRequestSchema, - DeleteCustomerByIdRequestSchema, - GetCustomerByIdRequestSchema, - UpdateCustomerByIdParamsRequestSchema, - UpdateCustomerByIdRequestSchema, -} from "../../../common/dto"; -import { getCustomerDependencies } from "../dependencies"; -import { - CreateCustomerController, - DeleteCustomerController, - GetCustomerController, - ListCustomersController, -} from "./controllers"; -import { UpdateCustomerController } from "./controllers/update-customer.controller"; +import { CustomerListRequestSchema, GetCustomerByIdRequestSchema } from "../../../common/dto"; +import { buildCustomerDependencies } from "../dependencies"; +import { GetCustomerController, ListCustomersController } from "./controllers"; export const customersRouter = (params: ModuleParams) => { const { app, database, baseRoutePath, logger } = params as { @@ -27,7 +14,7 @@ export const customersRouter = (params: ModuleParams) => { logger: ILogger; }; - const deps = getCustomerDependencies(params); + const deps = buildCustomerDependencies(params); const router: Router = Router({ mergeParams: true }); @@ -74,7 +61,7 @@ export const customersRouter = (params: ModuleParams) => { } ); - router.post( + /*router.post( "/", //checkTabContext, @@ -108,7 +95,7 @@ export const customersRouter = (params: ModuleParams) => { const controller = new DeleteCustomerController(useCase); return controller.execute(req, res, next); } - ); + ); */ app.use(`${baseRoutePath}/customers`, router); }; diff --git a/modules/customers/src/common/dto/response/customer-list.response.dto.ts b/modules/customers/src/common/dto/response/customer-list.response.dto.ts index 2f1b963d..bc4e63b1 100644 --- a/modules/customers/src/common/dto/response/customer-list.response.dto.ts +++ b/modules/customers/src/common/dto/response/customer-list.response.dto.ts @@ -5,6 +5,7 @@ export const CustomerListResponseSchema = createListViewResponseSchema( z.object({ id: z.uuid(), company_id: z.uuid(), + status: z.string(), reference: z.string(), is_company: z.string(), @@ -23,10 +24,9 @@ export const CustomerListResponseSchema = createListViewResponseSchema( fax: z.string(), website: z.string(), - legal_record: z.string(), + //legal_record: z.string(), + //default_taxes: z.string(), - default_taxes: z.string(), - status: z.string(), language_code: z.string(), currency_code: z.string(),