Uecko_ERP/modules/customer-invoices/src/api/infrastructure/mappers/domain/customer-invoice.mapper.ts

414 lines
12 KiB
TypeScript
Raw Normal View History

2025-09-26 18:09:14 +00:00
import { ISequelizeDomainMapper, MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
2025-09-04 17:57:04 +00:00
import {
CurrencyCode,
2025-10-28 17:52:30 +00:00
extractOrPushError,
2025-09-04 17:57:04 +00:00
LanguageCode,
2025-10-28 17:52:30 +00:00
maybeFromNullableVO,
2025-09-04 17:57:04 +00:00
Percentage,
TextValue,
2025-10-28 17:52:30 +00:00
toNullable,
2025-09-04 17:57:04 +00:00
UniqueID,
UtcDate,
2025-09-16 11:29:45 +00:00
ValidationErrorCollection,
ValidationErrorDetail,
2025-09-04 17:57:04 +00:00
} from "@repo/rdx-ddd";
2025-10-28 17:52:30 +00:00
import { Collection, isNullishOrEmpty, Maybe, Result } from "@repo/rdx-utils";
2025-06-12 06:55:17 +00:00
import {
CustomerInvoice,
2025-09-05 11:23:45 +00:00
CustomerInvoiceItems,
2025-06-12 06:55:17 +00:00
CustomerInvoiceNumber,
2025-09-03 10:41:12 +00:00
CustomerInvoiceProps,
2025-06-12 06:55:17 +00:00
CustomerInvoiceSerie,
CustomerInvoiceStatus,
2025-09-22 17:31:49 +00:00
InvoicePaymentMethod,
2025-09-11 12:05:50 +00:00
} from "../../../domain";
import { CustomerInvoiceCreationAttributes, CustomerInvoiceModel } from "../../sequelize";
2025-10-04 18:35:27 +00:00
import { CustomerInvoiceItemDomainMapper } from "./customer-invoice-item.mapper";
import { InvoiceRecipientDomainMapper } from "./invoice-recipient.mapper";
import { TaxesDomainMapper } from "./invoice-taxes.mapper";
2025-09-11 12:05:50 +00:00
2025-09-13 18:45:55 +00:00
export interface ICustomerInvoiceDomainMapper
2025-09-11 12:05:50 +00:00
extends ISequelizeDomainMapper<
2025-06-12 06:55:17 +00:00
CustomerInvoiceModel,
CustomerInvoiceCreationAttributes,
CustomerInvoice
> {}
2025-06-11 15:13:44 +00:00
2025-09-13 18:45:55 +00:00
export class CustomerInvoiceDomainMapper
2025-09-11 12:05:50 +00:00
extends SequelizeDomainMapper<
CustomerInvoiceModel,
CustomerInvoiceCreationAttributes,
CustomerInvoice
>
2025-09-13 18:45:55 +00:00
implements ICustomerInvoiceDomainMapper
2025-06-11 15:13:44 +00:00
{
2025-10-04 18:35:27 +00:00
private _itemsMapper: CustomerInvoiceItemDomainMapper;
private _recipientMapper: InvoiceRecipientDomainMapper;
private _taxesMapper: TaxesDomainMapper;
2025-06-11 15:13:44 +00:00
2025-09-10 16:06:29 +00:00
constructor(params: MapperParamsType) {
2025-06-11 15:13:44 +00:00
super();
2025-09-10 16:06:29 +00:00
2025-10-04 18:35:27 +00:00
this._itemsMapper = new CustomerInvoiceItemDomainMapper(params); // Instanciar el mapper de items
this._recipientMapper = new InvoiceRecipientDomainMapper();
this._taxesMapper = new TaxesDomainMapper(params);
2025-09-22 17:31:49 +00:00
}
private _mapAttributesToDomain(source: CustomerInvoiceModel, params?: MapperParamsType) {
2025-09-09 18:13:54 +00:00
const { errors } = params as {
errors: ValidationErrorDetail[];
};
const invoiceId = extractOrPushError(UniqueID.create(source.id), "id", errors);
const companyId = extractOrPushError(UniqueID.create(source.company_id), "company_id", errors);
const customerId = extractOrPushError(
UniqueID.create(source.customer_id),
"customer_id",
errors
);
const isProforma = Boolean(source.is_proforma);
const proformaId = extractOrPushError(
maybeFromNullableVO(source.proforma_id, (v) => UniqueID.create(v)),
"proforma_id",
errors
);
2025-09-09 18:13:54 +00:00
const status = extractOrPushError(
CustomerInvoiceStatus.create(source.status),
"status",
errors
);
const series = extractOrPushError(
2025-10-04 18:35:27 +00:00
maybeFromNullableVO(source.series, (v) => CustomerInvoiceSerie.create(v)),
"series",
2025-09-09 18:13:54 +00:00
errors
);
const invoiceNumber = extractOrPushError(
CustomerInvoiceNumber.create(source.invoice_number),
2025-09-09 18:13:54 +00:00
"invoice_number",
errors
);
2025-10-04 18:35:27 +00:00
// Fechas
2025-09-09 18:13:54 +00:00
const invoiceDate = extractOrPushError(
UtcDate.createFromISO(source.invoice_date),
"invoice_date",
errors
);
const operationDate = extractOrPushError(
2025-10-04 18:35:27 +00:00
maybeFromNullableVO(source.operation_date, (v) => UtcDate.createFromISO(v)),
2025-09-09 18:13:54 +00:00
"operation_date",
errors
);
2025-10-04 18:35:27 +00:00
// Idioma / divisa
const languageCode = extractOrPushError(
LanguageCode.create(source.language_code),
"language_code",
errors
);
const currencyCode = extractOrPushError(
CurrencyCode.create(source.currency_code),
"currency_code",
errors
);
// Textos opcionales
2025-09-26 18:09:14 +00:00
const reference = extractOrPushError(
maybeFromNullableVO(source.reference, (value) => Result.ok(String(value))),
"reference",
errors
);
2025-09-30 11:57:21 +00:00
const description = extractOrPushError(
maybeFromNullableVO(source.description, (value) => Result.ok(String(value))),
"description",
errors
);
2025-09-09 18:13:54 +00:00
const notes = extractOrPushError(
maybeFromNullableVO(source.notes, (value) => TextValue.create(value)),
"notes",
errors
);
2025-10-04 18:35:27 +00:00
// Método de pago (VO opcional con id + descripción)
let paymentMethod = Maybe.none<InvoicePaymentMethod>();
2025-09-09 18:13:54 +00:00
2025-10-04 18:35:27 +00:00
if (!isNullishOrEmpty(source.payment_method_id)) {
const paymentId = extractOrPushError(
UniqueID.create(String(source.payment_method_id)),
"paymentMethod.id",
errors
);
const paymentVO = extractOrPushError(
InvoicePaymentMethod.create(
{ paymentDescription: String(source.payment_method_description ?? "") },
paymentId ?? undefined
),
"payment_method_description",
errors
);
2025-09-09 18:13:54 +00:00
2025-10-04 18:35:27 +00:00
if (paymentVO) {
paymentMethod = Maybe.some(paymentVO);
}
}
// % descuento (VO)
2025-09-09 18:13:54 +00:00
const discountPercentage = extractOrPushError(
2025-09-10 18:14:19 +00:00
Percentage.create({
2025-10-04 18:35:27 +00:00
value: Number(source.discount_percentage_value ?? 0),
scale: Number(source.discount_percentage_scale ?? 2),
2025-09-10 18:14:19 +00:00
}),
"discount_percentage_value",
2025-09-09 18:13:54 +00:00
errors
);
return {
invoiceId,
companyId,
customerId,
isProforma,
proformaId,
2025-09-09 18:13:54 +00:00
status,
series,
invoiceNumber,
invoiceDate,
operationDate,
2025-09-26 18:09:14 +00:00
reference,
2025-09-30 11:57:21 +00:00
description,
2025-09-09 18:13:54 +00:00
notes,
languageCode,
currencyCode,
discountPercentage,
2025-10-04 18:35:27 +00:00
paymentMethod,
2025-09-09 18:13:54 +00:00
};
2025-06-11 15:13:44 +00:00
}
2025-06-12 06:55:17 +00:00
public mapToDomain(
source: CustomerInvoiceModel,
params?: MapperParamsType
): Result<CustomerInvoice, Error> {
2025-09-04 17:57:04 +00:00
try {
const errors: ValidationErrorDetail[] = [];
2025-09-10 16:06:29 +00:00
// 1) Valores escalares (atributos generales)
2025-09-22 17:31:49 +00:00
const attributes = this._mapAttributesToDomain(source, { errors, ...params });
2025-09-09 18:13:54 +00:00
2025-09-10 18:14:19 +00:00
// 2) Recipient (snapshot en la factura o include)
2025-09-09 18:13:54 +00:00
const recipientResult = this._recipientMapper.mapToDomain(source, {
2025-09-09 15:48:12 +00:00
errors,
2025-09-09 18:13:54 +00:00
attributes,
2025-09-09 15:48:12 +00:00
...params,
});
2025-09-04 17:57:04 +00:00
/*if (recipientResult.isFailure) {
2025-09-09 18:13:54 +00:00
errors.push({
path: "recipient",
2025-09-10 16:06:29 +00:00
2025-09-09 18:13:54 +00:00
message: recipientResult.error.message,
});
}*/
2025-09-09 18:13:54 +00:00
2025-09-10 18:14:19 +00:00
// 3) Items (colección)
2025-09-11 12:05:50 +00:00
const itemsResults = this._itemsMapper.mapToDomainCollection(
source.items,
source.items.length,
{
errors,
attributes,
...params,
}
);
2025-09-04 17:57:04 +00:00
/*if (itemsResults.isFailure) {
2025-09-09 18:13:54 +00:00
errors.push({
path: "items",
2025-11-04 11:24:14 +00:00
message: itemsResults.error.message,
2025-09-09 18:13:54 +00:00
});
}*/
2025-09-09 18:13:54 +00:00
2025-10-04 18:35:27 +00:00
// Nota: los impuestos a nivel factura (tabla customer_invoice_taxes) se derivan de los items.
// El agregado expone un getter `taxes` (derivado). No se incluye en las props.
2025-09-22 17:31:49 +00:00
2025-09-10 18:14:19 +00:00
// 5) Si hubo errores de mapeo, devolvemos colección de validación
2025-09-04 17:57:04 +00:00
if (errors.length > 0) {
2025-10-18 19:57:52 +00:00
return Result.fail(
new ValidationErrorCollection("Customer invoice mapping failed [mapToDomain]", errors)
);
2025-09-04 17:57:04 +00:00
}
2025-09-10 18:14:19 +00:00
// 6) Construcción del agregado (Dominio)
2025-09-09 18:13:54 +00:00
const recipient = recipientResult.data;
const items = CustomerInvoiceItems.create({
languageCode: attributes.languageCode!,
currencyCode: attributes.currencyCode!,
items: itemsResults.data.getAll(),
});
2025-09-04 17:57:04 +00:00
const invoiceProps: CustomerInvoiceProps = {
2025-09-09 18:13:54 +00:00
companyId: attributes.companyId!,
2025-09-09 15:48:12 +00:00
2025-09-09 18:13:54 +00:00
isProforma: attributes.isProforma,
proformaId: attributes.proformaId!,
2025-09-09 18:13:54 +00:00
status: attributes.status!,
series: attributes.series!,
invoiceNumber: attributes.invoiceNumber!,
invoiceDate: attributes.invoiceDate!,
operationDate: attributes.operationDate!,
2025-09-04 17:57:04 +00:00
2025-09-09 18:13:54 +00:00
customerId: attributes.customerId!,
2025-09-09 15:48:12 +00:00
recipient: recipient,
2025-09-08 17:24:38 +00:00
2025-09-26 18:09:14 +00:00
reference: attributes.reference!,
2025-09-30 11:57:21 +00:00
description: attributes.description!,
2025-09-09 18:13:54 +00:00
notes: attributes.notes!,
2025-09-04 17:57:04 +00:00
2025-09-09 18:13:54 +00:00
languageCode: attributes.languageCode!,
currencyCode: attributes.currencyCode!,
2025-09-04 17:57:04 +00:00
2025-09-10 18:14:19 +00:00
discountPercentage: attributes.discountPercentage!,
2025-09-04 17:57:04 +00:00
2025-10-04 18:35:27 +00:00
paymentMethod: attributes.paymentMethod!,
2025-09-22 17:31:49 +00:00
2025-09-09 18:13:54 +00:00
items,
2025-09-04 17:57:04 +00:00
};
2025-09-09 18:13:54 +00:00
const createResult = CustomerInvoice.create(invoiceProps, attributes.invoiceId);
if (createResult.isFailure) {
return Result.fail(
2025-10-18 19:57:52 +00:00
new ValidationErrorCollection("Customer invoice entity creation failed", [
{ path: "invoice", message: createResult.error.message },
])
2025-09-09 18:13:54 +00:00
);
}
return Result.ok(createResult.data);
2025-09-04 17:57:04 +00:00
} catch (err: unknown) {
return Result.fail(err as Error);
}
2025-06-11 15:13:44 +00:00
}
2025-06-12 06:55:17 +00:00
public mapToPersistence(
source: CustomerInvoice,
params?: MapperParamsType
2025-09-11 12:05:50 +00:00
): Result<CustomerInvoiceCreationAttributes, Error> {
2025-09-26 15:00:11 +00:00
const errors: ValidationErrorDetail[] = [];
// 1) Items
const itemsResult = this._itemsMapper.mapToPersistenceArray(source.items, {
errors,
parent: source,
...params,
});
2025-11-07 17:51:18 +00:00
2025-09-26 15:00:11 +00:00
if (itemsResult.isFailure) {
errors.push({
path: "items",
message: itemsResult.error.message,
});
}
2025-09-08 17:24:38 +00:00
2025-10-04 18:35:27 +00:00
// 2) Taxes
2025-10-28 17:52:30 +00:00
const taxesResult = this._taxesMapper.mapToPersistenceArray(new Collection(source.getTaxes()), {
2025-09-26 15:00:11 +00:00
errors,
parent: source,
...params,
});
2025-11-07 17:51:18 +00:00
2025-09-26 15:00:11 +00:00
if (taxesResult.isFailure) {
errors.push({
path: "taxes",
message: taxesResult.error.message,
});
}
2025-11-07 17:51:18 +00:00
// 3) Cliente
const recipient = this._recipientMapper.mapToPersistence(source.recipient, {
2025-10-03 19:01:38 +00:00
errors,
parent: source,
...params,
});
2025-09-26 18:09:14 +00:00
2025-11-07 17:51:18 +00:00
// 4) Si hubo errores de mapeo, devolvemos colección de validación
2025-09-26 18:09:14 +00:00
if (errors.length > 0) {
2025-10-18 19:57:52 +00:00
return Result.fail(
new ValidationErrorCollection("Customer invoice mapping to persistence failed", errors)
);
2025-09-26 18:09:14 +00:00
}
2025-11-07 17:51:18 +00:00
const items = itemsResult.data;
const taxes = taxesResult.data;
const allAmounts = source.getAllAmounts(); // Da los totales ya calculados
const invoiceValues: Partial<CustomerInvoiceCreationAttributes> = {
2025-10-04 18:35:27 +00:00
// Identificación
2025-09-05 11:23:45 +00:00
id: source.id.toPrimitive(),
company_id: source.companyId.toPrimitive(),
2025-10-04 18:35:27 +00:00
// Flags / estado / serie / número
2025-09-26 15:00:11 +00:00
is_proforma: source.isProforma,
proforma_id: toNullable(source.proformaId, (v) => v.toPrimitive()),
2025-09-05 11:23:45 +00:00
status: source.status.toPrimitive(),
2025-10-04 18:35:27 +00:00
series: toNullable(source.series, (v) => v.toPrimitive()),
invoice_number: source.invoiceNumber.toPrimitive(),
2025-10-04 18:35:27 +00:00
2025-09-04 17:57:04 +00:00
invoice_date: source.invoiceDate.toPrimitive(),
2025-10-04 18:35:27 +00:00
operation_date: toNullable(source.operationDate, (v) => v.toPrimitive()),
2025-09-26 15:00:11 +00:00
language_code: source.languageCode.toPrimitive(),
currency_code: source.currencyCode.toPrimitive(),
2025-09-05 11:23:45 +00:00
2025-09-26 18:09:14 +00:00
reference: toNullable(source.reference, (reference) => reference),
2025-09-30 11:57:21 +00:00
description: toNullable(source.description, (description) => description),
2025-10-04 18:35:27 +00:00
notes: toNullable(source.notes, (v) => v.toPrimitive()),
2025-09-05 11:23:45 +00:00
2025-09-26 15:00:11 +00:00
subtotal_amount_value: allAmounts.subtotalAmount.value,
subtotal_amount_scale: allAmounts.subtotalAmount.scale,
discount_percentage_value: source.discountPercentage.toPrimitive().value,
discount_percentage_scale: source.discountPercentage.toPrimitive().scale,
2025-09-05 11:23:45 +00:00
2025-11-07 17:51:18 +00:00
discount_amount_value: allAmounts.headerDiscountAmount.value,
discount_amount_scale: allAmounts.headerDiscountAmount.scale,
2025-09-05 11:23:45 +00:00
2025-09-26 15:00:11 +00:00
taxable_amount_value: allAmounts.taxableAmount.value,
taxable_amount_scale: allAmounts.taxableAmount.scale,
2025-09-05 11:23:45 +00:00
2025-09-26 15:00:11 +00:00
taxes_amount_value: allAmounts.taxesAmount.value,
taxes_amount_scale: allAmounts.taxesAmount.scale,
2025-09-08 17:24:38 +00:00
2025-09-26 15:00:11 +00:00
total_amount_value: allAmounts.totalAmount.value,
total_amount_scale: allAmounts.totalAmount.scale,
2025-09-09 18:13:54 +00:00
2025-09-26 15:00:11 +00:00
payment_method_id: toNullable(source.paymentMethod, (payment) => payment.toObjectString().id),
payment_method_description: toNullable(
source.paymentMethod,
(payment) => payment.toObjectString().payment_description
),
2025-09-26 18:09:14 +00:00
customer_id: source.customerId.toPrimitive(),
...recipient,
2025-10-04 18:35:27 +00:00
2025-09-26 18:09:14 +00:00
taxes,
items,
2025-09-26 15:00:11 +00:00
};
return Result.ok<CustomerInvoiceCreationAttributes>(
invoiceValues as CustomerInvoiceCreationAttributes
);
2025-06-11 15:13:44 +00:00
}
}