150 lines
3.6 KiB
TypeScript
150 lines
3.6 KiB
TypeScript
|
|
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<IMoneyValueProps> 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);
|
||
|
|
}
|
||
|
|
}
|