diff --git a/modules/customer-invoices/src/api/infrastructure/sequelize/customer-invoice.repository.ts b/modules/customer-invoices/src/api/infrastructure/sequelize/customer-invoice.repository.ts index 94223864..e1573ef0 100644 --- a/modules/customer-invoices/src/api/infrastructure/sequelize/customer-invoice.repository.ts +++ b/modules/customer-invoices/src/api/infrastructure/sequelize/customer-invoice.repository.ts @@ -122,7 +122,10 @@ export class CustomerInvoiceRepository transaction: Transaction ): Promise> { try { - const mapper: ICustomerInvoiceDomainMapper = this._registry.getDomainMapper("FULL"); + const mapper: ICustomerInvoiceDomainMapper = this._registry.getDomainMapper({ + resource: "customer-invoice", + }); + const { CustomerModel } = this._database.models; const row = await CustomerInvoiceModel.findOne({ @@ -182,7 +185,10 @@ export class CustomerInvoiceRepository transaction: Transaction ): Promise, Error>> { try { - const mapper: ICustomerInvoiceListMapper = this._registry.getQueryMapper("LIST"); + const mapper: ICustomerInvoiceListMapper = this._registry.getQueryMapper({ + resource: "customer-invoice", + query: "LIST", + }); const { CustomerModel } = this._database.models; const converter = new CriteriaToSequelizeConverter(); const query = converter.convert(criteria); diff --git a/modules/customers/src/api/domain/repositories/customer-repository.interface.ts b/modules/customers/src/api/domain/repositories/customer-repository.interface.ts index a3c4d170..d3d013ed 100644 --- a/modules/customers/src/api/domain/repositories/customer-repository.interface.ts +++ b/modules/customers/src/api/domain/repositories/customer-repository.interface.ts @@ -42,7 +42,7 @@ export interface ICustomerRepository { companyId: UniqueID, criteria: Criteria, transaction?: any - ): Promise>>; + ): Promise>>; /** * Elimina un Customer por su ID, dentro de una empresa. diff --git a/modules/customers/src/api/infrastructure/mappers/domain/customer.full.mapper.ts b/modules/customers/src/api/infrastructure/mappers/domain/customer.mapper.ts similarity index 98% rename from modules/customers/src/api/infrastructure/mappers/domain/customer.full.mapper.ts rename to modules/customers/src/api/infrastructure/mappers/domain/customer.mapper.ts index 3b9fd05c..720bc690 100644 --- a/modules/customers/src/api/infrastructure/mappers/domain/customer.full.mapper.ts +++ b/modules/customers/src/api/infrastructure/mappers/domain/customer.mapper.ts @@ -159,10 +159,7 @@ export class CustomerDomainMapper }); } - if (errors.length > 0) { - return Result.fail(new ValidationErrorCollection("Customer props mapping failed", errors)); - } - + // Now, create the PostalAddress VO const postalAddressProps = { street: street!, street2: street2!, @@ -178,6 +175,11 @@ export class CustomerDomainMapper errors ); + // Si hubo errores de mapeo, devolvemos colección de validación + if (errors.length > 0) { + return Result.fail(new ValidationErrorCollection("Customer props mapping failed", errors)); + } + const customerProps: CustomerProps = { companyId: companyId!, status: status!, diff --git a/modules/customers/src/api/infrastructure/mappers/domain/index.ts b/modules/customers/src/api/infrastructure/mappers/domain/index.ts index c0f84fc5..7f5fae75 100644 --- a/modules/customers/src/api/infrastructure/mappers/domain/index.ts +++ b/modules/customers/src/api/infrastructure/mappers/domain/index.ts @@ -1 +1 @@ -export * from "./customer.full.mapper"; +export * from "./customer.mapper"; diff --git a/modules/customers/src/api/infrastructure/mappers/queries/customer.list.mapper.ts b/modules/customers/src/api/infrastructure/mappers/queries/customer.list.mapper.ts new file mode 100644 index 00000000..fe643333 --- /dev/null +++ b/modules/customers/src/api/infrastructure/mappers/queries/customer.list.mapper.ts @@ -0,0 +1,212 @@ +import { + City, + Country, + CurrencyCode, + EmailAddress, + LanguageCode, + Name, + PhoneNumber, + PostalAddress, + PostalCode, + Province, + Street, + TINNumber, + TextValue, + URLAddress, + UniqueID, + maybeFromNullableVO, +} from "@repo/rdx-ddd"; + +import { + ISequelizeQueryMapper, + MapperParamsType, + SequelizeQueryMapper, + ValidationErrorCollection, + ValidationErrorDetail, + extractOrPushError, +} from "@erp/core/api"; + +import { Maybe, Result } from "@repo/rdx-utils"; +import { CustomerStatus } from "../../../domain"; +import { CustomerModel } from "../../sequelize"; + +export type CustomerListDTO = { + id: UniqueID; + companyId: UniqueID; + status: CustomerStatus; + reference: Maybe; + + isCompany: boolean; + name: Name; + tradeName: Maybe; + tin: Maybe; + + address: PostalAddress; + + email: Maybe; + phone: Maybe; + fax: Maybe; + website: Maybe; + + languageCode: LanguageCode; + currencyCode: CurrencyCode; +}; + +export interface ICustomerListMapper + extends ISequelizeQueryMapper {} + +export class CustomerListMapper + extends SequelizeQueryMapper + implements ICustomerListMapper +{ + public mapToDTO(raw: CustomerModel, params?: MapperParamsType): Result { + const errors: ValidationErrorDetail[] = []; + + // 1) Valores escalares (atributos generales) + const customerId = extractOrPushError(UniqueID.create(raw.id), "id", errors); + const companyId = extractOrPushError(UniqueID.create(raw.company_id), "company_id", errors); + + const isCompany = raw.is_company; + const status = extractOrPushError(CustomerStatus.create(raw.status), "status", errors); + const reference = extractOrPushError( + maybeFromNullableVO(raw.reference, (value) => Name.create(value)), + "reference", + errors + ); + + const name = extractOrPushError(Name.create(raw.name), "name", errors); + + const tradeName = extractOrPushError( + maybeFromNullableVO(raw.trade_name, (value) => Name.create(value)), + "trade_name", + errors + ); + + const tinNumber = extractOrPushError( + maybeFromNullableVO(raw.tin, (value) => TINNumber.create(value)), + "tin", + errors + ); + + const street = extractOrPushError( + maybeFromNullableVO(raw.street, (value) => Street.create(value)), + "street", + errors + ); + + const street2 = extractOrPushError( + maybeFromNullableVO(raw.street2, (value) => Street.create(value)), + "street2", + errors + ); + + const city = extractOrPushError( + maybeFromNullableVO(raw.city, (value) => City.create(value)), + "city", + errors + ); + + const province = extractOrPushError( + maybeFromNullableVO(raw.province, (value) => Province.create(value)), + "province", + errors + ); + + const postalCode = extractOrPushError( + maybeFromNullableVO(raw.postal_code, (value) => PostalCode.create(value)), + "postal_code", + errors + ); + + const country = extractOrPushError( + maybeFromNullableVO(raw.country, (value) => Country.create(value)), + "country", + errors + ); + + const emailAddress = extractOrPushError( + maybeFromNullableVO(raw.email, (value) => EmailAddress.create(value)), + "email", + errors + ); + + const phoneNumber = extractOrPushError( + maybeFromNullableVO(raw.phone, (value) => PhoneNumber.create(value)), + "phone", + errors + ); + + const faxNumber = extractOrPushError( + maybeFromNullableVO(raw.fax, (value) => PhoneNumber.create(value)), + "fax", + errors + ); + + const website = extractOrPushError( + maybeFromNullableVO(raw.website, (value) => URLAddress.create(value)), + "website", + errors + ); + + const legalRecord = extractOrPushError( + maybeFromNullableVO(raw.legal_record, (value) => TextValue.create(value)), + "legal_record", + errors + ); + + const languageCode = extractOrPushError( + LanguageCode.create(raw.language_code), + "language_code", + errors + ); + + const currencyCode = extractOrPushError( + CurrencyCode.create(raw.currency_code), + "currency_code", + errors + ); + + // Valores compuestos (objetos de valor / agregados) + const postalAddressProps = { + street: street!, + street2: street2!, + city: city!, + postalCode: postalCode!, + province: province!, + country: country!, + }; + + const postalAddress = extractOrPushError( + PostalAddress.create(postalAddressProps), + "address", + errors + ); + + // Si hubo errores de mapeo, devolvemos colección de validación + if (errors.length > 0) { + return Result.fail(new ValidationErrorCollection("Customer invoice mapping failed", errors)); + } + + return Result.ok({ + id: customerId!, + companyId: companyId!, + status: status!, + reference: reference!, + + isCompany: isCompany, + name: name!, + tradeName: tradeName!, + tin: tinNumber!, + + address: postalAddress!, + + email: emailAddress!, + phone: phoneNumber!, + fax: faxNumber!, + website: website!, + + languageCode: languageCode!, + currencyCode: currencyCode!, + }); + } +} diff --git a/modules/customers/src/api/infrastructure/mappers/queries/index.ts b/modules/customers/src/api/infrastructure/mappers/queries/index.ts index e69de29b..019cce62 100644 --- a/modules/customers/src/api/infrastructure/mappers/queries/index.ts +++ b/modules/customers/src/api/infrastructure/mappers/queries/index.ts @@ -0,0 +1 @@ +export * from "./customer.list.mapper"; diff --git a/modules/customers/src/api/infrastructure/sequelize/repositories/customer.repository.ts b/modules/customers/src/api/infrastructure/sequelize/repositories/customer.repository.ts index 9262858c..56939c63 100644 --- a/modules/customers/src/api/infrastructure/sequelize/repositories/customer.repository.ts +++ b/modules/customers/src/api/infrastructure/sequelize/repositories/customer.repository.ts @@ -4,6 +4,7 @@ import { UniqueID } from "@repo/rdx-ddd"; import { Collection, Result } from "@repo/rdx-utils"; import { Transaction } from "sequelize"; import { Customer, ICustomerRepository } from "../../../domain"; +import { CustomerListDTO, ICustomerDomainMapper, ICustomerListMapper } from "../../mappers"; import { CustomerModel } from "../models/customer.model"; export class CustomerRepository @@ -112,10 +113,11 @@ export class CustomerRepository companyId: UniqueID, criteria: Criteria, transaction?: Transaction - ): Promise>> { + ): Promise, Error>> { try { - const mapper: ICustomerDomainMapper = this._registry.getDomainMapper({ + const mapper: ICustomerListMapper = this._registry.getQueryMapper({ resource: "customer", + query: "LIST", }); const converter = new CriteriaToSequelizeConverter(); @@ -126,12 +128,12 @@ export class CustomerRepository company_id: companyId.toString(), }; - const instances = await CustomerModel.findAll({ + const { rows, count } = await CustomerModel.findAndCountAll({ ...query, transaction, }); - return mapper.mapArrayToDomain(instances); + return mapper.mapToDTOCollection(rows, count); } catch (err: unknown) { return Result.fail(translateSequelizeError(err)); }