Uecko_ERP/modules/customer-invoices/src/api/infrastructure/mappers/customer-invoice-item.mapper.ts
2025-09-10 18:06:29 +02:00

232 lines
6.6 KiB
TypeScript

import {
ISequelizeMapper,
MapperParamsType,
SequelizeMapper,
ValidationErrorCollection,
ValidationErrorDetail,
extractOrPushError,
} from "@erp/core/api";
import { UniqueID, maybeFromNullableVO } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { InferCreationAttributes } from "sequelize";
import {
CustomerInvoiceItem,
CustomerInvoiceItemDescription,
CustomerInvoiceProps,
ItemAmount,
ItemDiscount,
ItemQuantity,
ItemTaxes,
} from "../../domain";
import { CustomerInvoiceItemCreationAttributes, CustomerInvoiceItemModel } from "../sequelize";
import { ItemTaxesMapper } from "./item-taxes.mapper";
export interface ICustomerInvoiceItemMapper
extends ISequelizeMapper<
CustomerInvoiceItemModel,
CustomerInvoiceItemCreationAttributes,
CustomerInvoiceItem
> {}
export class CustomerInvoiceItemMapper
extends SequelizeMapper<
CustomerInvoiceItemModel,
CustomerInvoiceItemCreationAttributes,
CustomerInvoiceItem
>
implements ICustomerInvoiceItemMapper
{
private _taxesMapper: ItemTaxesMapper;
constructor(params: MapperParamsType) {
super();
this._taxesMapper = new ItemTaxesMapper(params);
}
private mapAttributesToDomain(source: CustomerInvoiceItemModel, params?: MapperParamsType) {
const { errors, index, attributes } = params as {
index: number;
errors: ValidationErrorDetail[];
attributes: Partial<CustomerInvoiceProps>;
};
const itemId = extractOrPushError(
UniqueID.create(source.item_id),
`items[${index}].item_id`,
errors
);
const description = extractOrPushError(
maybeFromNullableVO(source.description, (value) =>
CustomerInvoiceItemDescription.create(value)
),
`items[${index}].description`,
errors
);
const quantity = extractOrPushError(
maybeFromNullableVO(source.quantity_value, (value) => ItemQuantity.create({ value })),
`items[${index}].discount_percentage`,
errors
);
const unitAmount = extractOrPushError(
maybeFromNullableVO(source.unit_amount_value, (value) =>
ItemAmount.create({ value, currency_code: attributes.currencyCode!.code })
),
`items[${index}].unit_amount`,
errors
);
return {
itemId,
languageCode: attributes.languageCode,
currencyCode: attributes.currencyCode,
description,
quantity,
unitAmount,
};
}
public mapToDomain(
source: CustomerInvoiceItemModel,
params?: MapperParamsType
): Result<CustomerInvoiceItem, Error> {
const { errors, index, requireIncludes } = params as {
index: number;
requireIncludes: boolean;
errors: ValidationErrorDetail[];
attributes: Partial<CustomerInvoiceProps>;
};
// 1) Valores escalares (atributos generales)
const attributes = this.mapAttributesToDomain(source, params);
// 2) Comprobar relaciones
if (requireIncludes) {
if (!source.taxes) {
errors.push({
path: `items[${index}].taxes`,
message: "Taxes not included in query (requireIncludes=true)",
});
}
}
// 3) Importes
const discountPercentage = extractOrPushError(
maybeFromNullableVO(source.discount_percentage_value, (value) =>
ItemDiscount.create({ value })
),
`items[${index}].discount_percentage`,
errors
);
// 4) Taxes (colección a nivel de item/línea)
const taxesResults = this._taxesMapper.mapArrayToDomain(source.taxes, {
attributes,
...params,
});
if (taxesResults.isFailure) {
errors.push({
path: `taxes[${index}].discount_percentage`,
message: taxesResults.error.message,
});
}
// 5) Construcción del elemento de dominio
const taxes = ItemTaxes.create({
items: taxesResults.data.getAll(),
});
const createResult = CustomerInvoiceItem.create(
{
languageCode: attributes.languageCode!,
currencyCode: attributes.currencyCode!,
description: attributes.description!,
quantity: attributes.quantity!,
unitAmount: attributes.unitAmount!,
discountPercentage: discountPercentage!,
taxes,
},
attributes.itemId
);
if (createResult.isFailure) {
return Result.fail(
new ValidationErrorCollection("Invoice item entity creation failed", [
{ path: `items[${index}]`, message: createResult.error.message },
])
);
}
return createResult;
}
public mapToPersistence(
source: CustomerInvoiceItem,
params?: MapperParamsType
): InferCreationAttributes<CustomerInvoiceItemModel, {}> {
throw new Error("not implemented");
/*
const { index, sourceParent } = params as {
index: number;
sourceParent: CustomerInvoice;
};
return {
item_id: source.id.toPrimitive(),
invoice_id: sourceParent.id.toPrimitive(),
position: index,
description: toNullable(source.description, (description) => description.toPrimitive()),
quantity_value: toNullable(source.quantity, (value) => value.toPrimitive().value),
quantity_scale: source.quantity.match(
(value) => value.toPrimitive().scale,
() => ItemQuantity.DEFAULT_SCALE
),
unit_amount_value: toNullable(source.unitAmount, (value) => value.toPrimitive().value),
unit_amount_scale: source.unitAmount.match(
(value) => value.toPrimitive().scale,
() => ItemAmount.DEFAULT_SCALE
),
subtotal_amount_value: source.subtotalAmount.toPrimitive().value,
subtotal_amount_scale: source.subtotalAmount.toPrimitive().scale,
discount_percentage_value: toNullable(
source.discountPercentage,
(value) => value.toPrimitive().value
),
discount_percentage_scale: source.discountPercentage.match(
(value) => value.toPrimitive().scale,
() => ItemAmount.DEFAULT_SCALE
),
discount_amount_value: toNullable(
source.discountAmount,
(value) => value.toPrimitive().value
),
discount_amount_scale: source.discountAmount.match(
(value) => value.toPrimitive().scale,
() => ItemDiscount.DEFAULT_SCALE
),
taxable_amount_value: source.taxableAmount.toPrimitive().value,
taxable_amount_scale: source.taxableAmount.toPrimitive().value,
taxes_amount_value: source.taxesAmount.toPrimitive().value,
taxes_amount_scale: source.taxesAmount.toPrimitive().value,
total_amount_value: source.totalAmount.toPrimitive().value,
total_amount_scale: source.totalAmount.toPrimitive().scale,
};*/
}
}