2025-09-10 11:57:15 +00:00
|
|
|
|
import { CurrencyCode, DomainEntity, LanguageCode, Percentage, UniqueID } from "@repo/rdx-ddd";
|
2025-09-03 10:41:12 +00:00
|
|
|
|
import { Maybe, Result } from "@repo/rdx-utils";
|
|
|
|
|
|
import {
|
|
|
|
|
|
CustomerInvoiceItemDescription,
|
2025-09-05 11:23:45 +00:00
|
|
|
|
ItemAmount,
|
|
|
|
|
|
ItemDiscount,
|
|
|
|
|
|
ItemQuantity,
|
2025-09-03 10:41:12 +00:00
|
|
|
|
} from "../../value-objects";
|
2025-10-12 09:14:33 +00:00
|
|
|
|
import { ItemTaxTotal, ItemTaxes } from "../item-taxes";
|
2025-09-03 10:41:12 +00:00
|
|
|
|
|
|
|
|
|
|
export interface CustomerInvoiceItemProps {
|
|
|
|
|
|
description: Maybe<CustomerInvoiceItemDescription>;
|
2025-09-05 11:23:45 +00:00
|
|
|
|
quantity: Maybe<ItemQuantity>; // Cantidad de unidades
|
|
|
|
|
|
unitAmount: Maybe<ItemAmount>; // Precio unitario en la moneda de la factura
|
2025-09-10 11:57:15 +00:00
|
|
|
|
|
2025-09-05 11:23:45 +00:00
|
|
|
|
discountPercentage: Maybe<ItemDiscount>; // % descuento
|
2025-09-03 10:41:12 +00:00
|
|
|
|
|
2025-09-10 11:57:15 +00:00
|
|
|
|
taxes: ItemTaxes;
|
2025-09-07 19:55:12 +00:00
|
|
|
|
|
2025-09-03 10:41:12 +00:00
|
|
|
|
languageCode: LanguageCode;
|
|
|
|
|
|
currencyCode: CurrencyCode;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-05 11:23:45 +00:00
|
|
|
|
export interface ICustomerInvoiceItem {
|
2025-09-19 09:29:49 +00:00
|
|
|
|
isNonValued: boolean;
|
|
|
|
|
|
|
2025-09-05 11:23:45 +00:00
|
|
|
|
description: Maybe<CustomerInvoiceItemDescription>;
|
|
|
|
|
|
|
|
|
|
|
|
quantity: Maybe<ItemQuantity>; // Cantidad de unidades
|
|
|
|
|
|
unitAmount: Maybe<ItemAmount>; // Precio unitario en la moneda de la factura
|
|
|
|
|
|
|
|
|
|
|
|
discountPercentage: Maybe<ItemDiscount>; // % descuento
|
|
|
|
|
|
|
2025-09-10 11:57:15 +00:00
|
|
|
|
taxes: ItemTaxes;
|
2025-09-05 11:23:45 +00:00
|
|
|
|
|
|
|
|
|
|
languageCode: LanguageCode;
|
|
|
|
|
|
currencyCode: CurrencyCode;
|
|
|
|
|
|
|
2025-09-10 11:57:15 +00:00
|
|
|
|
getSubtotalAmount(): ItemAmount;
|
|
|
|
|
|
getDiscountAmount(): ItemAmount;
|
2025-09-05 11:23:45 +00:00
|
|
|
|
|
2025-09-10 11:57:15 +00:00
|
|
|
|
getTaxableAmount(): ItemAmount;
|
|
|
|
|
|
getTaxesAmount(): ItemAmount;
|
|
|
|
|
|
getTotalAmount(): ItemAmount;
|
2025-09-05 11:23:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export class CustomerInvoiceItem
|
|
|
|
|
|
extends DomainEntity<CustomerInvoiceItemProps>
|
|
|
|
|
|
implements ICustomerInvoiceItem
|
|
|
|
|
|
{
|
2025-09-19 09:29:49 +00:00
|
|
|
|
protected _isNonValued!: boolean;
|
|
|
|
|
|
|
2025-09-03 10:41:12 +00:00
|
|
|
|
public static create(
|
|
|
|
|
|
props: CustomerInvoiceItemProps,
|
|
|
|
|
|
id?: UniqueID
|
|
|
|
|
|
): Result<CustomerInvoiceItem, Error> {
|
|
|
|
|
|
const item = new CustomerInvoiceItem(props, id);
|
|
|
|
|
|
|
|
|
|
|
|
// Reglas de negocio / validaciones
|
|
|
|
|
|
// ...
|
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
|
|
|
|
return Result.ok(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-19 09:29:49 +00:00
|
|
|
|
protected constructor(props: CustomerInvoiceItemProps, id?: UniqueID) {
|
|
|
|
|
|
super(props, id);
|
|
|
|
|
|
|
|
|
|
|
|
this._isNonValued = this.quantity.isNone() || this.unitAmount.isNone();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
get isNonValued(): boolean {
|
|
|
|
|
|
return this._isNonValued;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-03 10:41:12 +00:00
|
|
|
|
get description(): Maybe<CustomerInvoiceItemDescription> {
|
|
|
|
|
|
return this.props.description;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-05 11:23:45 +00:00
|
|
|
|
get quantity(): Maybe<ItemQuantity> {
|
2025-09-03 10:41:12 +00:00
|
|
|
|
return this.props.quantity;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-05 11:23:45 +00:00
|
|
|
|
get unitAmount(): Maybe<ItemAmount> {
|
2025-09-04 17:57:04 +00:00
|
|
|
|
return this.props.unitAmount;
|
2025-09-03 10:41:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-04 17:57:04 +00:00
|
|
|
|
get discountPercentage(): Maybe<Percentage> {
|
|
|
|
|
|
return this.props.discountPercentage;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 11:57:15 +00:00
|
|
|
|
get languageCode(): LanguageCode {
|
|
|
|
|
|
return this.props.languageCode;
|
2025-09-03 10:41:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 11:57:15 +00:00
|
|
|
|
get currencyCode(): CurrencyCode {
|
|
|
|
|
|
return this.props.currencyCode;
|
2025-09-05 11:23:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 11:57:15 +00:00
|
|
|
|
get taxes(): ItemTaxes {
|
|
|
|
|
|
return this.props.taxes;
|
2025-09-05 11:23:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 11:57:15 +00:00
|
|
|
|
getProps(): CustomerInvoiceItemProps {
|
|
|
|
|
|
return this.props;
|
2025-09-03 10:41:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 11:57:15 +00:00
|
|
|
|
toPrimitive() {
|
|
|
|
|
|
return this.getProps();
|
2025-09-03 10:41:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-12 09:14:33 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @private
|
|
|
|
|
|
* @summary Calcula el importe de descuento a partir del subtotal y el porcentaje.
|
|
|
|
|
|
* @param subtotalAmount - Importe subtotal.
|
|
|
|
|
|
* @returns El importe de descuento calculado.
|
|
|
|
|
|
*/
|
2025-09-17 17:37:41 +00:00
|
|
|
|
private _getDiscountAmount(subtotalAmount: ItemAmount): ItemAmount {
|
|
|
|
|
|
const discount = this.discountPercentage.match(
|
|
|
|
|
|
(percentage) => percentage,
|
|
|
|
|
|
() => ItemDiscount.zero()
|
|
|
|
|
|
);
|
|
|
|
|
|
return subtotalAmount.percentage(discount);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-12 09:14:33 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @private
|
|
|
|
|
|
* @summary Calcula el importe imponible restando el descuento al subtotal.
|
|
|
|
|
|
* @param subtotalAmount - Importe subtotal.
|
|
|
|
|
|
* @param discountAmount - Importe de descuento.
|
|
|
|
|
|
* @returns El importe imponible resultante.
|
|
|
|
|
|
*/
|
2025-09-17 17:37:41 +00:00
|
|
|
|
private _getTaxableAmount(subtotalAmount: ItemAmount, discountAmount: ItemAmount): ItemAmount {
|
|
|
|
|
|
return subtotalAmount.subtract(discountAmount);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-12 09:14:33 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @private
|
|
|
|
|
|
* @summary Calcula el importe total de impuestos sobre la base imponible.
|
|
|
|
|
|
* @param taxableAmount - Importe imponible.
|
|
|
|
|
|
* @returns El importe de impuestos calculado.
|
|
|
|
|
|
*/
|
2025-09-17 17:37:41 +00:00
|
|
|
|
private _getTaxesAmount(taxableAmount: ItemAmount): ItemAmount {
|
|
|
|
|
|
return this.props.taxes.getTaxesAmount(taxableAmount);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-12 09:14:33 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @private
|
|
|
|
|
|
* @summary Calcula el importe total sumando base imponible e impuestos.
|
|
|
|
|
|
* @param taxableAmount - Importe imponible.
|
|
|
|
|
|
* @param taxesAmount - Importe de impuestos.
|
|
|
|
|
|
* @returns El importe total del ítem.
|
|
|
|
|
|
*/
|
2025-09-17 17:37:41 +00:00
|
|
|
|
private _getTotalAmount(taxableAmount: ItemAmount, taxesAmount: ItemAmount): ItemAmount {
|
|
|
|
|
|
return taxableAmount.add(taxesAmount);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-12 09:14:33 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @summary Calcula el subtotal del ítem (cantidad × importe unitario).
|
|
|
|
|
|
* @returns Un `ItemAmount` con el subtotal del ítem.
|
|
|
|
|
|
* @remarks
|
|
|
|
|
|
* Si la cantidad o el importe unitario no están definidos, se asumen valores cero.
|
|
|
|
|
|
*/
|
2025-09-10 11:57:15 +00:00
|
|
|
|
public getSubtotalAmount(): ItemAmount {
|
|
|
|
|
|
const curCode = this.currencyCode.code;
|
|
|
|
|
|
const quantity = this.quantity.match(
|
|
|
|
|
|
(quantity) => quantity,
|
|
|
|
|
|
() => ItemQuantity.zero()
|
|
|
|
|
|
);
|
|
|
|
|
|
const unitAmount = this.unitAmount.match(
|
|
|
|
|
|
(unitAmount) => unitAmount,
|
|
|
|
|
|
() => ItemAmount.zero(curCode)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
return unitAmount.multiply(quantity);
|
2025-09-03 10:41:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-12 09:14:33 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @summary Calcula el importe total de descuento del ítem.
|
|
|
|
|
|
* @returns Un `ItemAmount` con el importe descontado.
|
|
|
|
|
|
*/
|
2025-09-10 11:57:15 +00:00
|
|
|
|
public getDiscountAmount(): ItemAmount {
|
2025-09-17 17:37:41 +00:00
|
|
|
|
return this._getDiscountAmount(this.getSubtotalAmount());
|
2025-09-03 10:41:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-12 09:14:33 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @summary Calcula el importe imponible (subtotal − descuento).
|
|
|
|
|
|
* @returns Un `ItemAmount` con la base imponible del ítem.
|
|
|
|
|
|
*/
|
2025-09-10 11:57:15 +00:00
|
|
|
|
public getTaxableAmount(): ItemAmount {
|
2025-09-17 17:37:41 +00:00
|
|
|
|
return this._getTaxableAmount(this.getSubtotalAmount(), this.getDiscountAmount());
|
2025-09-03 10:41:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-12 09:14:33 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @summary Calcula el importe total de impuestos aplicados al ítem.
|
|
|
|
|
|
* @returns Un `ItemAmount` con el total de impuestos.
|
|
|
|
|
|
*/
|
2025-09-10 11:57:15 +00:00
|
|
|
|
public getTaxesAmount(): ItemAmount {
|
|
|
|
|
|
return this._getTaxesAmount(this.getTaxableAmount());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-12 09:14:33 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @summary Calcula el importe total final del ítem (base imponible + impuestos).
|
|
|
|
|
|
* @returns Un `ItemAmount` con el importe total.
|
|
|
|
|
|
*/
|
2025-09-10 11:57:15 +00:00
|
|
|
|
public getTotalAmount(): ItemAmount {
|
|
|
|
|
|
const taxableAmount = this.getTaxableAmount();
|
2025-09-17 17:37:41 +00:00
|
|
|
|
const taxesAmount = this._getTaxesAmount(taxableAmount);
|
|
|
|
|
|
|
|
|
|
|
|
return this._getTotalAmount(taxableAmount, taxesAmount);
|
2025-09-10 11:57:15 +00:00
|
|
|
|
}
|
2025-09-03 10:41:12 +00:00
|
|
|
|
|
2025-10-12 09:14:33 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @summary Obtiene los importes de impuestos agrupados por tipo de impuesto.
|
|
|
|
|
|
* @returns Una colección con la base imponible y el importe de impuestos por cada tipo.
|
|
|
|
|
|
*/
|
|
|
|
|
|
public getTaxesAmountByTaxes(): ItemTaxTotal[] {
|
2025-09-26 15:00:11 +00:00
|
|
|
|
return this.taxes.getTaxesAmountByTaxes(this.getTaxableAmount());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-12 09:14:33 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @summary Devuelve todos los importes calculados del ítem en un único objeto.
|
|
|
|
|
|
* @returns Un objeto con las propiedades:
|
|
|
|
|
|
* - `subtotalAmount`
|
|
|
|
|
|
* - `discountAmount`
|
|
|
|
|
|
* - `taxableAmount`
|
|
|
|
|
|
* - `taxesAmount`
|
|
|
|
|
|
* - `totalAmount`
|
|
|
|
|
|
* @remarks
|
|
|
|
|
|
* Este método es útil para mostrar todos los cálculos en la interfaz de usuario
|
|
|
|
|
|
* o serializar el ítem con sus valores calculados.
|
|
|
|
|
|
*/
|
2025-09-10 11:57:15 +00:00
|
|
|
|
public getAllAmounts() {
|
2025-09-17 17:37:41 +00:00
|
|
|
|
const subtotalAmount = this.getSubtotalAmount();
|
|
|
|
|
|
const discountAmount = this._getDiscountAmount(subtotalAmount);
|
|
|
|
|
|
const taxableAmount = this._getTaxableAmount(subtotalAmount, discountAmount);
|
|
|
|
|
|
const taxesAmount = this._getTaxesAmount(taxableAmount);
|
|
|
|
|
|
const totalAmount = this._getTotalAmount(taxableAmount, taxesAmount);
|
|
|
|
|
|
|
2025-09-10 11:57:15 +00:00
|
|
|
|
return {
|
2025-09-17 17:37:41 +00:00
|
|
|
|
subtotalAmount,
|
|
|
|
|
|
discountAmount,
|
|
|
|
|
|
taxableAmount,
|
|
|
|
|
|
taxesAmount,
|
|
|
|
|
|
totalAmount,
|
2025-09-10 11:57:15 +00:00
|
|
|
|
};
|
2025-09-03 10:41:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|