import { ISequelizeDomainMapper, MapperParamsType, SequelizeDomainMapper, 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 { 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) { const { errors, index, attributes } = params as { index: number; errors: ValidationErrorDetail[]; attributes: Partial; }; 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 { const { errors, index, requireIncludes } = params as { index: number; requireIncludes: boolean; errors: ValidationErrorDetail[]; attributes: Partial; }; // 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.mapToDomainCollection( source.taxes, source.taxes.length, { 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 ): Result, Error> { 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, };*/ } }