Mejorado VO Tax

This commit is contained in:
David Arranz 2025-11-04 12:24:14 +01:00
parent 54e47fee1b
commit b9bfdc02aa
7 changed files with 46 additions and 18 deletions

View File

@ -77,8 +77,17 @@ export class Tax extends ValueObject<TaxProps> {
*/
static createFromCode(code: string, provider: TaxCatalogProvider): Result<Tax, Error> {
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);

View File

@ -24,9 +24,16 @@ export abstract class SequelizeDomainMapper<TModel extends Model, TModelAttribut
return Result.ok(new Collection([], totalCount));
}
const items = _source.map(
(value, index) => 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);

View File

@ -1,6 +1,6 @@
{
"name": "@erp/customer-invoices",
"version": "0.0.4",
"version": "0.0.5",
"private": true,
"type": "module",
"sideEffects": false,

View File

@ -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

View File

@ -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

View File

@ -226,7 +226,7 @@ export class CustomerInvoiceDomainMapper
if (itemsResults.isFailure) {
errors.push({
path: "items",
message: recipientResult.error.message,
message: itemsResults.error.message,
});
}

View File

@ -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) {