233 lines
6.6 KiB
TypeScript
233 lines
6.6 KiB
TypeScript
import { ISequelizeDomainMapper, MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
|
import {
|
|
extractOrPushError,
|
|
maybeFromNullableVO,
|
|
toNullable,
|
|
UniqueID,
|
|
ValidationErrorCollection,
|
|
ValidationErrorDetail,
|
|
} from "@repo/rdx-ddd";
|
|
import { Result } from "@repo/rdx-utils";
|
|
import {
|
|
CustomerInvoice,
|
|
CustomerInvoiceItem,
|
|
CustomerInvoiceItemDescription,
|
|
CustomerInvoiceItemProps,
|
|
CustomerInvoiceProps,
|
|
ItemAmount,
|
|
ItemDiscount,
|
|
ItemQuantity,
|
|
ItemTaxes,
|
|
} from "../../../domain";
|
|
import { CustomerInvoiceItemCreationAttributes, CustomerInvoiceItemModel } from "../../sequelize";
|
|
import { ItemTaxesDomainMapper } from "./item-taxes.mapper";
|
|
|
|
export interface ICustomerInvoiceItemDomainMapper
|
|
extends ISequelizeDomainMapper<
|
|
CustomerInvoiceItemModel,
|
|
CustomerInvoiceItemCreationAttributes,
|
|
CustomerInvoiceItem
|
|
> {}
|
|
|
|
export class CustomerInvoiceItemDomainMapper
|
|
extends SequelizeDomainMapper<
|
|
CustomerInvoiceItemModel,
|
|
CustomerInvoiceItemCreationAttributes,
|
|
CustomerInvoiceItem
|
|
>
|
|
implements ICustomerInvoiceItemDomainMapper
|
|
{
|
|
private _taxesMapper: ItemTaxesDomainMapper;
|
|
|
|
constructor(params: MapperParamsType) {
|
|
super();
|
|
this._taxesMapper = new ItemTaxesDomainMapper(params);
|
|
}
|
|
|
|
private mapAttributesToDomain(
|
|
source: CustomerInvoiceItemModel,
|
|
params?: MapperParamsType
|
|
): Partial<CustomerInvoiceItemProps> & { itemId?: UniqueID } {
|
|
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, (v) => CustomerInvoiceItemDescription.create(v)),
|
|
`items[${index}].description`,
|
|
errors
|
|
);
|
|
|
|
const quantity = extractOrPushError(
|
|
maybeFromNullableVO(source.quantity_value, (v) => ItemQuantity.create({ value: v })),
|
|
`items[${index}].quantity`,
|
|
errors
|
|
);
|
|
|
|
const unitAmount = extractOrPushError(
|
|
maybeFromNullableVO(source.unit_amount_value, (value) =>
|
|
ItemAmount.create({ value, currency_code: attributes.currencyCode?.code })
|
|
),
|
|
`items[${index}].unit_amount`,
|
|
errors
|
|
);
|
|
|
|
const discountPercentage = extractOrPushError(
|
|
maybeFromNullableVO(source.discount_percentage_value, (v) =>
|
|
ItemDiscount.create({ value: v })
|
|
),
|
|
`items[${index}].discount_percentage`,
|
|
errors
|
|
);
|
|
|
|
return {
|
|
itemId,
|
|
|
|
languageCode: attributes.languageCode,
|
|
currencyCode: attributes.currencyCode,
|
|
description,
|
|
quantity,
|
|
unitAmount,
|
|
discountPercentage,
|
|
};
|
|
}
|
|
|
|
public mapToDomain(
|
|
source: CustomerInvoiceItemModel,
|
|
params?: MapperParamsType
|
|
): Result<CustomerInvoiceItem, Error> {
|
|
const { errors, index } = params as {
|
|
index: number;
|
|
errors: ValidationErrorDetail[];
|
|
attributes: Partial<CustomerInvoiceProps>;
|
|
};
|
|
|
|
// 1) Valores escalares (atributos generales)
|
|
const attributes = this.mapAttributesToDomain(source, params);
|
|
|
|
// 2) Taxes (colección a nivel de item/línea)
|
|
const taxesResults = this._taxesMapper.mapToDomainCollection(
|
|
source.taxes,
|
|
source.taxes.length,
|
|
{
|
|
attributes,
|
|
...params,
|
|
}
|
|
);
|
|
|
|
if (taxesResults.isFailure) {
|
|
errors.push({
|
|
path: "taxes",
|
|
message: taxesResults.error.message,
|
|
});
|
|
}
|
|
|
|
// 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
|
|
const createResult = CustomerInvoiceItem.create(
|
|
{
|
|
languageCode: attributes.languageCode!,
|
|
currencyCode: attributes.currencyCode!,
|
|
description: attributes.description!,
|
|
quantity: attributes.quantity!,
|
|
unitAmount: attributes.unitAmount!,
|
|
discountPercentage: attributes.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
|
|
): Result<CustomerInvoiceItemCreationAttributes, Error> {
|
|
const { errors, index, parent } = params as {
|
|
index: number;
|
|
parent: CustomerInvoice;
|
|
errors: ValidationErrorDetail[];
|
|
};
|
|
|
|
const taxesResults = this._taxesMapper.mapToPersistenceArray(source.taxes, {
|
|
...params,
|
|
parent: source,
|
|
});
|
|
|
|
if (taxesResults.isFailure) {
|
|
errors.push({
|
|
path: "taxes",
|
|
message: taxesResults.error.message,
|
|
});
|
|
}
|
|
|
|
const allAmounts = source.getAllAmounts();
|
|
|
|
return Result.ok({
|
|
item_id: source.id.toPrimitive(),
|
|
invoice_id: parent.id.toPrimitive(),
|
|
position: index,
|
|
|
|
description: toNullable(source.description, (v) => v.toPrimitive()),
|
|
|
|
quantity_value: toNullable(source.quantity, (v) => v.toPrimitive().value),
|
|
quantity_scale:
|
|
toNullable(source.quantity, (v) => v.toPrimitive().scale) ?? ItemQuantity.DEFAULT_SCALE,
|
|
|
|
unit_amount_value: toNullable(source.unitAmount, (v) => v.toPrimitive().value),
|
|
unit_amount_scale:
|
|
toNullable(source.unitAmount, (v) => v.toPrimitive().scale) ?? ItemAmount.DEFAULT_SCALE,
|
|
|
|
subtotal_amount_value: allAmounts.subtotalAmount.value,
|
|
subtotal_amount_scale: allAmounts.subtotalAmount.scale,
|
|
|
|
discount_percentage_value: toNullable(
|
|
source.discountPercentage,
|
|
(v) => v.toPrimitive().value
|
|
),
|
|
discount_percentage_scale:
|
|
toNullable(source.discountPercentage, (v) => v.toPrimitive().scale) ??
|
|
ItemDiscount.DEFAULT_SCALE,
|
|
|
|
discount_amount_value: allAmounts.discountAmount.value,
|
|
discount_amount_scale: allAmounts.discountAmount.scale,
|
|
|
|
taxable_amount_value: allAmounts.taxableAmount.value,
|
|
taxable_amount_scale: allAmounts.taxableAmount.scale,
|
|
|
|
taxes_amount_value: allAmounts.taxesAmount.value,
|
|
taxes_amount_scale: allAmounts.taxesAmount.scale,
|
|
|
|
total_amount_value: allAmounts.totalAmount.value,
|
|
total_amount_scale: allAmounts.totalAmount.scale,
|
|
|
|
taxes: taxesResults.data,
|
|
});
|
|
}
|
|
}
|