Uecko_ERP/modules/customer-invoices/src/api/domain/entities/customer-invoice-items/customer-invoice-item.ts

245 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { type CurrencyCode, DomainEntity, type LanguageCode, type UniqueID } from "@repo/rdx-ddd";
import { type Maybe, Result } from "@repo/rdx-utils";
import {
type CustomerInvoiceItemDescription,
ItemAmount,
ItemDiscount,
ItemQuantity,
} from "../../value-objects";
import type { ItemTaxGroup } from "../../value-objects/item-tax-group";
export interface CustomerInvoiceItemProps {
description: Maybe<CustomerInvoiceItemDescription>;
quantity: Maybe<ItemQuantity>; // Cantidad de unidades
unitAmount: Maybe<ItemAmount>; // Precio unitario en la moneda de la factura
discountPercentage: Maybe<ItemDiscount>; // % descuento
taxes: ItemTaxGroup;
languageCode: LanguageCode;
currencyCode: CurrencyCode;
}
export interface ICustomerInvoiceItem {
isValued: boolean;
description: Maybe<CustomerInvoiceItemDescription>;
quantity: Maybe<ItemQuantity>; // Cantidad de unidades
unitAmount: Maybe<ItemAmount>; // Precio unitario en la moneda de la factura
discountPercentage: Maybe<ItemDiscount>; // % descuento
taxes: ItemTaxGroup;
languageCode: LanguageCode;
currencyCode: CurrencyCode;
getSubtotalAmount(): ItemAmount;
getDiscountAmount(): ItemAmount;
getTaxableAmount(): ItemAmount;
getTaxesAmount(): ItemAmount;
getTotalAmount(): ItemAmount;
}
export class CustomerInvoiceItem
extends DomainEntity<CustomerInvoiceItemProps>
implements ICustomerInvoiceItem
{
protected _isValued!: boolean;
public static create(
props: CustomerInvoiceItemProps,
id?: UniqueID
): Result<CustomerInvoiceItem, Error> {
const item = new CustomerInvoiceItem(props, id);
// Reglas de negocio / validaciones
// ...
// ...
return Result.ok(item);
}
protected constructor(props: CustomerInvoiceItemProps, id?: UniqueID) {
super(props, id);
this._isValued = this.quantity.isSome() || this.unitAmount.isSome();
}
get isValued(): boolean {
return this._isValued;
}
get description() {
return this.props.description;
}
get quantity() {
return this.props.quantity;
}
get unitAmount() {
return this.props.unitAmount;
}
get discountPercentage() {
return this.props.discountPercentage;
}
get languageCode() {
return this.props.languageCode;
}
get currencyCode() {
return this.props.currencyCode;
}
get taxes() {
return this.props.taxes;
}
getProps(): CustomerInvoiceItemProps {
return this.props;
}
toPrimitive() {
return this.getProps();
}
/**
* @private
* @summary Calcula el importe de descuento a partir del subtotal y el porcentaje.
* @param subtotalAmount - Importe subtotal.
* @returns El importe de descuento calculado.
*/
private _getDiscountAmount(subtotalAmount: ItemAmount): ItemAmount {
const discount = this.discountPercentage.match(
(discount) => discount,
() => ItemDiscount.zero()
);
return subtotalAmount.percentage(discount);
}
/**
* @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.
*/
private _getTaxableAmount(subtotalAmount: ItemAmount, discountAmount: ItemAmount): ItemAmount {
return subtotalAmount.subtract(discountAmount);
}
/* importes individuales: iva / rec / ret */
private _getIndividualTaxAmounts(taxableAmount: ItemAmount) {
return this.props.taxes.calculateAmounts(taxableAmount);
}
/**
* @private
* @summary Calcula el importe total de impuestos sobre la base imponible.
* @param taxableAmount - Importe imponible.
* @returns El importe de impuestos calculado.
*/
private _getTaxesAmount(taxableAmount: ItemAmount): ItemAmount {
const { ivaAmount, recAmount, retentionAmount } = this._getIndividualTaxAmounts(taxableAmount);
return ivaAmount.add(recAmount).add(retentionAmount); // retención ya es negativa
}
/**
* @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.
*/
private _getTotalAmount(taxableAmount: ItemAmount, taxesAmount: ItemAmount): ItemAmount {
return taxableAmount.add(taxesAmount);
}
/**
* @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.
*/
public getSubtotalAmount(): ItemAmount {
const qty = this.quantity.match(
(quantity) => quantity,
() => ItemQuantity.zero()
);
const unit = this.unitAmount.match(
(unitAmount) => unitAmount,
() => ItemAmount.zero(this.currencyCode.code)
);
return unit.multiply(qty);
}
/**
* @summary Calcula el importe total de descuento del ítem.
* @returns Un `ItemAmount` con el importe descontado.
*/
public getDiscountAmount(): ItemAmount {
return this._getDiscountAmount(this.getSubtotalAmount());
}
/**
* @summary Calcula el importe imponible (subtotal descuento).
* @returns Un `ItemAmount` con la base imponible del ítem.
*/
public getTaxableAmount(): ItemAmount {
return this._getTaxableAmount(this.getSubtotalAmount(), this.getDiscountAmount());
}
/* importes individuales: iva / rec / ret */
public getIndividualTaxAmounts() {
return this._getIndividualTaxAmounts(this.getTaxableAmount());
}
/**
* @summary Calcula el importe total de impuestos aplicados al ítem.
* @returns Un `ItemAmount` con el total de impuestos.
*/
public getTaxesAmount(): ItemAmount {
return this._getTaxesAmount(this.getTaxableAmount());
}
/**
* @summary Calcula el importe total final del ítem (base imponible + impuestos).
* @returns Un `ItemAmount` con el importe total.
*/
public getTotalAmount(): ItemAmount {
const taxableAmount = this.getTaxableAmount();
const taxesAmount = this._getTaxesAmount(taxableAmount);
return this._getTotalAmount(taxableAmount, taxesAmount);
}
/**
* @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.
*/
public getAllAmounts() {
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);
return {
subtotalAmount,
discountAmount,
taxableAmount,
taxesAmount,
totalAmount,
};
}
}