Mejorado VO Tax
This commit is contained in:
parent
54e47fee1b
commit
b9bfdc02aa
@ -77,8 +77,17 @@ export class Tax extends ValueObject<TaxProps> {
|
|||||||
*/
|
*/
|
||||||
static createFromCode(code: string, provider: TaxCatalogProvider): Result<Tax, Error> {
|
static createFromCode(code: string, provider: TaxCatalogProvider): Result<Tax, Error> {
|
||||||
const normalized = (code ?? "").trim().toLowerCase();
|
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);
|
const maybeItem = provider.findByCode(normalized);
|
||||||
|
|||||||
@ -24,9 +24,16 @@ export abstract class SequelizeDomainMapper<TModel extends Model, TModelAttribut
|
|||||||
return Result.ok(new Collection([], totalCount));
|
return Result.ok(new Collection([], totalCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
const items = _source.map(
|
const items: TEntity[] = [];
|
||||||
(value, index) => this.mapToDomain(value as TModel, { index, ...params }).data
|
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));
|
return Result.ok(new Collection(items, totalCount));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return Result.fail(error as Error);
|
return Result.fail(error as Error);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@erp/customer-invoices",
|
"name": "@erp/customer-invoices",
|
||||||
"version": "0.0.4",
|
"version": "0.0.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
|||||||
@ -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 { ModuleParams, validateRequest } from "@erp/core/api";
|
||||||
import { ILogger } from "@repo/rdx-logger";
|
import { ILogger } from "@repo/rdx-logger";
|
||||||
import { Application, NextFunction, Request, Response, Router } from "express";
|
import { Application, NextFunction, Request, Response, Router } from "express";
|
||||||
@ -31,9 +31,8 @@ export const customerInvoicesRouter = (params: ModuleParams) => {
|
|||||||
const deps = buildCustomerInvoiceDependencies(params);
|
const deps = buildCustomerInvoiceDependencies(params);
|
||||||
|
|
||||||
const router: Router = Router({ mergeParams: true });
|
const router: Router = Router({ mergeParams: true });
|
||||||
|
if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "production") {
|
||||||
// 🔐 Autenticación + Tenancy para TODO el router
|
// 🔐 Autenticación + Tenancy para TODO el router
|
||||||
if (process.env.NODE_ENV === "development") {
|
|
||||||
router.use(
|
router.use(
|
||||||
(req: Request, res: Response, next: NextFunction) =>
|
(req: Request, res: Response, next: NextFunction) =>
|
||||||
mockUser(req as RequestWithAuth, res, next) // Debe ir antes de las rutas protegidas
|
mockUser(req as RequestWithAuth, res, next) // Debe ir antes de las rutas protegidas
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { ISequelizeDomainMapper, MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
import { ISequelizeDomainMapper, MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
||||||
import {
|
import {
|
||||||
UniqueID,
|
|
||||||
ValidationErrorCollection,
|
|
||||||
ValidationErrorDetail,
|
|
||||||
extractOrPushError,
|
extractOrPushError,
|
||||||
maybeFromNullableVO,
|
maybeFromNullableVO,
|
||||||
toNullable,
|
toNullable,
|
||||||
|
UniqueID,
|
||||||
|
ValidationErrorCollection,
|
||||||
|
ValidationErrorDetail,
|
||||||
} from "@repo/rdx-ddd";
|
} from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
import {
|
import {
|
||||||
@ -74,7 +74,7 @@ export class CustomerInvoiceItemDomainMapper
|
|||||||
|
|
||||||
const unitAmount = extractOrPushError(
|
const unitAmount = extractOrPushError(
|
||||||
maybeFromNullableVO(source.unit_amount_value, (value) =>
|
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`,
|
`items[${index}].unit_amount`,
|
||||||
errors
|
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());
|
const taxes = ItemTaxes.create(taxesResults.data.getAll());
|
||||||
|
|
||||||
// 3) Construcción del elemento de dominio
|
// 3) Construcción del elemento de dominio
|
||||||
|
|||||||
@ -226,7 +226,7 @@ export class CustomerInvoiceDomainMapper
|
|||||||
if (itemsResults.isFailure) {
|
if (itemsResults.isFailure) {
|
||||||
errors.push({
|
errors.push({
|
||||||
path: "items",
|
path: "items",
|
||||||
message: recipientResult.error.message,
|
message: itemsResults.error.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { JsonTaxCatalogProvider } from "@erp/core";
|
||||||
import {
|
import {
|
||||||
ISequelizeDomainMapper,
|
ISequelizeDomainMapper,
|
||||||
MapperParamsType,
|
MapperParamsType,
|
||||||
@ -5,13 +6,11 @@ import {
|
|||||||
Tax,
|
Tax,
|
||||||
} from "@erp/core/api";
|
} from "@erp/core/api";
|
||||||
|
|
||||||
import { JsonTaxCatalogProvider } from "@erp/core";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
extractOrPushError,
|
||||||
UniqueID,
|
UniqueID,
|
||||||
ValidationErrorCollection,
|
ValidationErrorCollection,
|
||||||
ValidationErrorDetail,
|
ValidationErrorDetail,
|
||||||
extractOrPushError,
|
|
||||||
} from "@repo/rdx-ddd";
|
} from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
import { CustomerInvoiceItem } from "../../../domain";
|
import { CustomerInvoiceItem } from "../../../domain";
|
||||||
@ -65,6 +64,13 @@ export class ItemTaxesDomainMapper
|
|||||||
errors
|
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
|
// Creación del objeto de dominio
|
||||||
const createResult = Tax.create(tax!);
|
const createResult = Tax.create(tax!);
|
||||||
if (createResult.isFailure) {
|
if (createResult.isFailure) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user