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

370 lines
10 KiB
TypeScript
Raw Normal View History

2025-09-03 10:41:12 +00:00
import {
ISequelizeMapper,
MapperParamsType,
SequelizeMapper,
ValidationErrorCollection,
ValidationErrorDetail,
extractOrPushError,
} from "@erp/core/api";
2025-09-04 17:57:04 +00:00
import {
CurrencyCode,
LanguageCode,
Percentage,
TextValue,
UniqueID,
UtcDate,
maybeFromNullableVO,
} from "@repo/rdx-ddd";
2025-06-11 15:13:44 +00:00
import { 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,
} from "../../domain";
2025-09-09 18:13:54 +00:00
import { InvoiceTaxes } from "../../domain/entities/invoice-taxes";
2025-06-11 15:13:44 +00:00
import { CustomerInvoiceCreationAttributes, CustomerInvoiceModel } from "../sequelize";
2025-06-12 06:55:17 +00:00
import { CustomerInvoiceItemMapper } from "./customer-invoice-item.mapper";
2025-09-09 15:48:12 +00:00
import { InvoiceRecipientMapper } from "./invoice-recipient.mapper";
import { TaxesMapper } from "./taxes.mapper";
2025-06-11 15:13:44 +00:00
export interface ICustomerInvoiceMapper
2025-06-12 06:55:17 +00:00
extends ISequelizeMapper<
CustomerInvoiceModel,
CustomerInvoiceCreationAttributes,
CustomerInvoice
> {}
2025-06-11 15:13:44 +00:00
export class CustomerInvoiceMapper
extends SequelizeMapper<CustomerInvoiceModel, CustomerInvoiceCreationAttributes, CustomerInvoice>
implements ICustomerInvoiceMapper
{
2025-09-04 17:57:04 +00:00
private _itemsMapper: CustomerInvoiceItemMapper;
2025-09-09 15:48:12 +00:00
private _recipientMapper: InvoiceRecipientMapper;
private _taxesMapper: TaxesMapper;
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
this._itemsMapper = new CustomerInvoiceItemMapper(params); // Instanciar el mapper de items
2025-09-09 15:48:12 +00:00
this._recipientMapper = new InvoiceRecipientMapper();
2025-09-09 18:13:54 +00:00
this._taxesMapper = new TaxesMapper(params);
}
private mapAttributesToDomain(source: CustomerInvoiceModel, params?: MapperParamsType) {
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 status = extractOrPushError(
CustomerInvoiceStatus.create(source.status),
"status",
errors
);
const series = extractOrPushError(
maybeFromNullableVO(source.series, (value) => CustomerInvoiceSerie.create(value)),
"serie",
errors
);
const invoiceNumber = extractOrPushError(
CustomerInvoiceNumber.create(source.invoice_number),
"invoice_number",
errors
);
const invoiceDate = extractOrPushError(
UtcDate.createFromISO(source.invoice_date),
"invoice_date",
errors
);
const operationDate = extractOrPushError(
maybeFromNullableVO(source.operation_date, (value) => UtcDate.createFromISO(value)),
"operation_date",
errors
);
const notes = extractOrPushError(
maybeFromNullableVO(source.notes, (value) => TextValue.create(value)),
"notes",
errors
);
const languageCode = extractOrPushError(
LanguageCode.create(source.language_code),
"language_code",
errors
);
const currencyCode = extractOrPushError(
CurrencyCode.create(source.currency_code),
"currency_code",
errors
);
const discountPercentage = extractOrPushError(
2025-09-10 10:27:07 +00:00
maybeFromNullableVO(source.discount_percentage_value, (value) =>
Percentage.create({
value: value,
scale: source.discount_percentage_scale,
})
),
2025-09-09 18:13:54 +00:00
"discount_percentage",
errors
);
return {
invoiceId,
companyId,
customerId,
isProforma,
status,
series,
invoiceNumber,
invoiceDate,
operationDate,
notes,
languageCode,
currencyCode,
discountPercentage,
};
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-09 18:13:54 +00:00
const attributes = this.mapAttributesToDomain(source, { errors, ...params });
2025-09-10 16:06:29 +00:00
// 2) Comprobar relaciones
2025-09-09 18:13:54 +00:00
const requireIncludes = Boolean(params?.requireIncludes);
if (requireIncludes) {
if (!source.items) {
errors.push({
path: "items",
message: "Items not included in query (requireIncludes=true)",
});
}
if (!source.taxes) {
errors.push({
path: "taxes",
message: "Taxes not included in query (requireIncludes=true)",
});
}
if (attributes.isProforma && !source.current_customer) {
errors.push({
path: "current_customer",
message: "Current customer not included in query (requireIncludes=true)",
});
}
}
// 3) Recipient (snapshot en la factura o include)
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
2025-09-09 18:13:54 +00:00
if (recipientResult.isFailure) {
errors.push({
path: "recipient",
2025-09-10 16:06:29 +00:00
2025-09-09 18:13:54 +00:00
message: recipientResult.error.message,
});
}
// 4) Items (colección)
const itemsResults = this._itemsMapper.mapArrayToDomain(source.items, {
2025-09-04 17:57:04 +00:00
errors,
2025-09-09 18:13:54 +00:00
attributes,
2025-09-04 17:57:04 +00:00
...params,
});
2025-09-09 18:13:54 +00:00
if (itemsResults.isFailure) {
errors.push({
path: "items",
message: recipientResult.error.message,
});
}
// 5) Taxes (colección a nivel factura)
const taxesResults = this._taxesMapper.mapArrayToDomain(source.taxes, {
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-08 17:24:38 +00:00
2025-09-09 18:13:54 +00:00
if (taxesResults.isFailure) {
errors.push({
path: "taxes",
2025-09-10 16:06:29 +00:00
message: taxesResults.error.message,
2025-09-09 18:13:54 +00:00
});
}
// 6) Si hubo errores de mapeo, devolvemos colección de validación
2025-09-04 17:57:04 +00:00
if (errors.length > 0) {
return Result.fail(
2025-09-09 18:13:54 +00:00
new ValidationErrorCollection("Customer invoice mapping failed", errors)
2025-09-04 17:57:04 +00:00
);
}
2025-09-09 18:13:54 +00:00
// 7) Construcción del agregado (Dominio)
const recipient = recipientResult.data;
const taxes = InvoiceTaxes.create({
items: taxesResults.data.getAll(),
});
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,
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-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 16:06:29 +00:00
//discountPercentage: attributes.discountPercentage!,
2025-09-04 17:57:04 +00:00
2025-09-09 18:13:54 +00:00
taxes,
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(
new ValidationErrorCollection("Customer invoice entity creation failed", [
{ path: "invoice", message: createResult.error.message },
])
);
}
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
): CustomerInvoiceCreationAttributes {
2025-09-10 16:06:29 +00:00
throw new Error("not implemented");
/*const items = this._itemsMapper.mapCollectionToPersistence(source.items, params);
2025-06-11 15:13:44 +00:00
2025-09-09 15:48:12 +00:00
const customer = source.recipient.match(
(recipient) =>
({
customer_id: recipient.id.toPrimitive(),
customer_tin: recipient.tin.toPrimitive(),
customer_name: recipient.name.toPrimitive(),
customer_street: toNullable(recipient.address.street, (street) => street.toPrimitive()),
customer_street2: toNullable(recipient.address.street2, (street2) =>
street2.toPrimitive()
),
customer_city: toNullable(recipient.address.city, (city) => city.toPrimitive()),
customer_province: toNullable(recipient.address.province, (province) =>
province.toPrimitive()
),
customer_postal_code: toNullable(recipient.address.postalCode, (postalCode) =>
postalCode.toPrimitive()
),
customer_country: toNullable(recipient.address.country, (country) =>
country.toPrimitive()
),
}) as any,
() => ({
customer_id: source.customerId.toPrimitive(),
customer_tin: null,
customer_name: null,
customer_street: null,
customer_street2: null,
customer_city: null,
customer_province: null,
customer_postal_code: null,
customer_country: null,
})
) as any;
2025-09-08 17:24:38 +00:00
2025-06-11 15:13:44 +00:00
return {
2025-09-05 11:23:45 +00:00
id: source.id.toPrimitive(),
company_id: source.companyId.toPrimitive(),
status: source.status.toPrimitive(),
series: toNullable(source.series, (series) => series.toPrimitive()),
2025-06-26 11:32:55 +00:00
invoice_number: source.invoiceNumber.toPrimitive(),
2025-09-04 17:57:04 +00:00
invoice_date: source.invoiceDate.toPrimitive(),
2025-06-11 15:13:44 +00:00
2025-09-05 11:23:45 +00:00
operation_date: toNullable(source.operationDate, (operationDate) =>
operationDate.toPrimitive()
),
notes: toNullable(source.notes, (notes) => notes.toPrimitive()),
language_code: source.languageCode.code,
currency_code: source.currencyCode.code,
subtotal_amount_value: 0, //subtotal.amount,
subtotal_amount_scale: 2, //subtotal.scale,
2025-09-08 17:24:38 +00:00
discount_percentage_value: source.discountPercentage.value,
2025-09-05 11:23:45 +00:00
discount_percentage_scale: source.discountPercentage.scale,
discount_amount_value: source.discountAmount.value,
2025-09-08 17:24:38 +00:00
discount_amount_scale: source.discountAmount.scale,
taxable_amount_value: source.taxableAmount.value,
2025-09-09 18:13:54 +00:00
taxable_amount_scale: source.taxableAmount.scale,
taxes_amount_value: source.taxAmount.value,
taxes_amount_scale: source.taxAmount.scale,
2025-06-11 15:13:44 +00:00
2025-09-05 11:23:45 +00:00
total_amount_value: 0, //total.amount,
total_amount_scale: 2, //total.scale,
2025-06-11 15:13:44 +00:00
items,
2025-09-08 17:24:38 +00:00
...customer,
2025-09-10 16:06:29 +00:00
};*/
2025-06-11 15:13:44 +00:00
}
}