diff --git a/modules/core/src/api/domain/value-objects/tax.ts b/modules/core/src/api/domain/value-objects/tax.ts index bc17f307..4cbdece9 100644 --- a/modules/core/src/api/domain/value-objects/tax.ts +++ b/modules/core/src/api/domain/value-objects/tax.ts @@ -77,8 +77,17 @@ export class Tax extends ValueObject { */ static createFromCode(code: string, provider: TaxCatalogProvider): Result { const normalized = (code ?? "").trim().toLowerCase(); - if (!normalized || !Tax.CODE_REGEX.test(normalized)) { - return Result.fail(new Error(`Código de impuesto inválido: "${code}"`)); + + const schema = z + .string() + .min(1, "El código del impuesto es obligatorio.") + .max(40, "El código del impuesto no puede exceder 40 caracteres.") + .regex(Tax.CODE_REGEX, "El código contiene caracteres no permitidos."); + + const validationResult = schema.safeParse(normalized); + + if (!validationResult.success) { + return Result.fail(new Error(validationResult.error.issues.map((e) => e.message).join(", "))); } const maybeItem = provider.findByCode(normalized); diff --git a/modules/core/src/api/infrastructure/sequelize/mappers/sequelize-domain-mapper.ts b/modules/core/src/api/infrastructure/sequelize/mappers/sequelize-domain-mapper.ts index a196f383..c02961ff 100644 --- a/modules/core/src/api/infrastructure/sequelize/mappers/sequelize-domain-mapper.ts +++ b/modules/core/src/api/infrastructure/sequelize/mappers/sequelize-domain-mapper.ts @@ -24,9 +24,16 @@ export abstract class SequelizeDomainMapper this.mapToDomain(value as TModel, { index, ...params }).data - ); + const items: TEntity[] = []; + for (let index = 0; index < _source.length; index++) { + const value = _source[index]; + const result = this.mapToDomain(value as TModel, { index, ...params }); + if (result.isFailure) { + return Result.fail(result.error); + } + items.push(result.data); + } + return Result.ok(new Collection(items, totalCount)); } catch (error) { return Result.fail(error as Error); diff --git a/modules/customer-invoices/package.json b/modules/customer-invoices/package.json index 3ce0fa6e..9033e3c4 100644 --- a/modules/customer-invoices/package.json +++ b/modules/customer-invoices/package.json @@ -1,6 +1,6 @@ { "name": "@erp/customer-invoices", - "version": "0.0.4", + "version": "0.0.5", "private": true, "type": "module", "sideEffects": false, diff --git a/modules/customer-invoices/src/api/infrastructure/express/customer-invoices.routes.ts b/modules/customer-invoices/src/api/infrastructure/express/customer-invoices.routes.ts index a5e55674..8543ff7a 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/customer-invoices.routes.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/customer-invoices.routes.ts @@ -1,4 +1,4 @@ -import { RequestWithAuth, enforceTenant, enforceUser, mockUser } from "@erp/auth/api"; +import { enforceTenant, enforceUser, mockUser, RequestWithAuth } from "@erp/auth/api"; import { ModuleParams, validateRequest } from "@erp/core/api"; import { ILogger } from "@repo/rdx-logger"; import { Application, NextFunction, Request, Response, Router } from "express"; @@ -31,9 +31,8 @@ export const customerInvoicesRouter = (params: ModuleParams) => { const deps = buildCustomerInvoiceDependencies(params); const router: Router = Router({ mergeParams: true }); - - // 🔐 Autenticación + Tenancy para TODO el router - if (process.env.NODE_ENV === "development") { + if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "production") { + // 🔐 Autenticación + Tenancy para TODO el router router.use( (req: Request, res: Response, next: NextFunction) => mockUser(req as RequestWithAuth, res, next) // Debe ir antes de las rutas protegidas diff --git a/modules/customer-invoices/src/api/infrastructure/mappers/domain/customer-invoice-item.mapper.ts b/modules/customer-invoices/src/api/infrastructure/mappers/domain/customer-invoice-item.mapper.ts index 1e3d0fe4..f858b835 100644 --- a/modules/customer-invoices/src/api/infrastructure/mappers/domain/customer-invoice-item.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/mappers/domain/customer-invoice-item.mapper.ts @@ -1,11 +1,11 @@ import { ISequelizeDomainMapper, MapperParamsType, SequelizeDomainMapper } from "@erp/core/api"; import { - UniqueID, - ValidationErrorCollection, - ValidationErrorDetail, extractOrPushError, maybeFromNullableVO, toNullable, + UniqueID, + ValidationErrorCollection, + ValidationErrorDetail, } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; import { @@ -74,7 +74,7 @@ export class CustomerInvoiceItemDomainMapper const unitAmount = extractOrPushError( maybeFromNullableVO(source.unit_amount_value, (value) => - ItemAmount.create({ value, currency_code: attributes.currencyCode!.code }) + ItemAmount.create({ value, currency_code: attributes.currencyCode?.code }) ), `items[${index}].unit_amount`, errors @@ -130,6 +130,13 @@ export class CustomerInvoiceItemDomainMapper }); } + // Si hubo errores de mapeo, devolvemos colección de validación + if (errors.length > 0) { + return Result.fail( + new ValidationErrorCollection("Customer invoice item mapping failed [mapToDomain]", errors) + ); + } + const taxes = ItemTaxes.create(taxesResults.data.getAll()); // 3) Construcción del elemento de dominio diff --git a/modules/customer-invoices/src/api/infrastructure/mappers/domain/customer-invoice.mapper.ts b/modules/customer-invoices/src/api/infrastructure/mappers/domain/customer-invoice.mapper.ts index c6492b89..cb91f7ed 100644 --- a/modules/customer-invoices/src/api/infrastructure/mappers/domain/customer-invoice.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/mappers/domain/customer-invoice.mapper.ts @@ -226,7 +226,7 @@ export class CustomerInvoiceDomainMapper if (itemsResults.isFailure) { errors.push({ path: "items", - message: recipientResult.error.message, + message: itemsResults.error.message, }); } diff --git a/modules/customer-invoices/src/api/infrastructure/mappers/domain/item-taxes.mapper.ts b/modules/customer-invoices/src/api/infrastructure/mappers/domain/item-taxes.mapper.ts index 8b82dfcb..7187fe58 100644 --- a/modules/customer-invoices/src/api/infrastructure/mappers/domain/item-taxes.mapper.ts +++ b/modules/customer-invoices/src/api/infrastructure/mappers/domain/item-taxes.mapper.ts @@ -1,3 +1,4 @@ +import { JsonTaxCatalogProvider } from "@erp/core"; import { ISequelizeDomainMapper, MapperParamsType, @@ -5,13 +6,11 @@ import { Tax, } from "@erp/core/api"; -import { JsonTaxCatalogProvider } from "@erp/core"; - import { + extractOrPushError, UniqueID, ValidationErrorCollection, ValidationErrorDetail, - extractOrPushError, } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; import { CustomerInvoiceItem } from "../../../domain"; @@ -65,6 +64,13 @@ export class ItemTaxesDomainMapper errors ); + // Si hubo errores de mapeo, devolvemos colección de validación + if (errors.length > 0) { + return Result.fail( + new ValidationErrorCollection("Invoice item tax mapping failed [mapToDomain]", errors) + ); + } + // Creación del objeto de dominio const createResult = Tax.create(tax!); if (createResult.isFailure) {