import { Result } from "@common/helpers"; import DineroFactory, { Currency, Dinero } from "dinero.js"; import { ValueObject } from "./value-object"; const DEFAULT_SCALE = 2; type CurrencyData = Currency; interface IMoneyValueProps { amount: number; scale: number; currency_code: string; } interface IMoneyValue { amount: number; scale: number; currency: Dinero.Currency; getValue(): IMoneyValueProps; convertScale(newScale: number): MoneyValue; add(addend: MoneyValue): MoneyValue; subtract(subtrahend: MoneyValue): MoneyValue; multiply(multiplier: number): MoneyValue; divide(divisor: number): MoneyValue; equalsTo(comparator: MoneyValue): boolean; greaterThan(comparator: MoneyValue): boolean; lessThan(comparator: MoneyValue): boolean; isZero(): boolean; isPositive(): boolean; isNegative(): boolean; format(locale: string): string; } export class MoneyValue extends ValueObject implements IMoneyValue { private readonly value: Dinero; static create(props: IMoneyValueProps) { return Result.ok(new MoneyValue(props)); } constructor(props: IMoneyValueProps) { super(props); const { amount, scale, currency_code } = props; this.value = Object.freeze( DineroFactory({ amount, precision: scale, currency: currency_code as Currency, }) ); // 🔒 Garantiza inmutabilidad } get amount(): number { return this.value.getAmount() / Math.pow(10, this.value.getPrecision()); } get currency(): CurrencyData { return this.value.getCurrency(); } get scale(): number { return this.value.getPrecision(); } getValue(): IMoneyValueProps { return this.props; } convertScale(newScale: number): MoneyValue { const factor = Math.pow(10, newScale - this.scale); const newAmount = Math.round(this.amount * factor); return new MoneyValue({ amount: newAmount, scale: newScale, currency_code: this.currency, }); } add(addend: MoneyValue): MoneyValue { return new MoneyValue({ amount: this.value.add(addend.value).getAmount(), scale: this.scale, currency_code: this.currency, }); } subtract(subtrahend: MoneyValue): MoneyValue { return new MoneyValue({ amount: this.value.subtract(subtrahend.value).getAmount(), scale: this.scale, currency_code: this.currency, }); } multiply(multiplier: number): MoneyValue { return new MoneyValue({ amount: this.value.multiply(multiplier).getAmount(), scale: this.scale, currency_code: this.currency, }); } divide(divisor: number): MoneyValue { return new MoneyValue({ amount: this.value.divide(divisor).getAmount(), scale: this.scale, currency_code: this.currency, }); } equalsTo(comparator: MoneyValue): boolean { return this.value.equalsTo(comparator.value); } greaterThan(comparator: MoneyValue): boolean { return this.value.greaterThan(comparator.value); } lessThan(comparator: MoneyValue): boolean { return this.value.lessThan(comparator.value); } isZero(): boolean { return this.value.isZero(); } isPositive(): boolean { return this.amount > 0; } isNegative(): boolean { return this.amount < 0; } format(locale: string): string { const amount = this.amount; const currency = this.currency; const scale = this.scale; return new Intl.NumberFormat(locale, { style: "currency", currency: currency, minimumFractionDigits: scale, maximumFractionDigits: scale, useGrouping: true, }).format(amount); } }