Uecko_ERP/apps/server/src/common/domain/value-objects/money-value.ts

158 lines
3.8 KiB
TypeScript
Raw Normal View History

2025-02-24 19:00:28 +00:00
import { Result } from "@common/helpers";
import DineroFactory, { Currency, Dinero } from "dinero.js";
import { ValueObject } from "./value-object";
const DEFAULT_SCALE = 2;
type CurrencyData = Currency;
2025-02-25 10:16:34 +00:00
export type RoundingMode =
| "HALF_ODD"
| "HALF_EVEN"
| "HALF_UP"
| "HALF_DOWN"
| "HALF_TOWARDS_ZERO"
| "HALF_AWAY_FROM_ZERO"
| "DOWN";
2025-02-24 19:00:28 +00:00
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 {
2025-02-25 10:16:34 +00:00
private readonly dinero: Dinero;
2025-02-24 19:00:28 +00:00
static create(props: IMoneyValueProps) {
return Result.ok(new MoneyValue(props));
}
constructor(props: IMoneyValueProps) {
super(props);
const { amount, scale, currency_code } = props;
2025-02-25 10:16:34 +00:00
this.dinero = Object.freeze(
2025-02-24 19:00:28 +00:00
DineroFactory({
amount,
precision: scale,
currency: currency_code as Currency,
})
); // 🔒 Garantiza inmutabilidad
}
get amount(): number {
2025-02-25 10:16:34 +00:00
return this.dinero.getAmount() / Math.pow(10, this.dinero.getPrecision());
2025-02-24 19:00:28 +00:00
}
get currency(): CurrencyData {
2025-02-25 10:16:34 +00:00
return this.dinero.getCurrency();
2025-02-24 19:00:28 +00:00
}
get scale(): number {
2025-02-25 10:16:34 +00:00
return this.dinero.getPrecision();
2025-02-24 19:00:28 +00:00
}
getValue(): IMoneyValueProps {
return this.props;
}
2025-02-25 10:16:34 +00:00
convertScale(newScale: number, roundingMode: RoundingMode = "HALF_UP"): MoneyValue {
const _newDinero = this.dinero.convertPrecision(newScale, roundingMode);
2025-02-24 19:00:28 +00:00
return new MoneyValue({
2025-02-25 10:16:34 +00:00
amount: _newDinero.getAmount(),
scale: _newDinero.getPrecision(),
currency_code: _newDinero.getCurrency(),
2025-02-24 19:00:28 +00:00
});
}
add(addend: MoneyValue): MoneyValue {
return new MoneyValue({
2025-02-25 10:16:34 +00:00
amount: this.dinero.add(addend.dinero).getAmount(),
2025-02-24 19:00:28 +00:00
scale: this.scale,
currency_code: this.currency,
});
}
subtract(subtrahend: MoneyValue): MoneyValue {
return new MoneyValue({
2025-02-25 10:16:34 +00:00
amount: this.dinero.subtract(subtrahend.dinero).getAmount(),
2025-02-24 19:00:28 +00:00
scale: this.scale,
currency_code: this.currency,
});
}
multiply(multiplier: number): MoneyValue {
return new MoneyValue({
2025-02-25 10:16:34 +00:00
amount: this.dinero.multiply(multiplier).getAmount(),
2025-02-24 19:00:28 +00:00
scale: this.scale,
currency_code: this.currency,
});
}
divide(divisor: number): MoneyValue {
return new MoneyValue({
2025-02-25 10:16:34 +00:00
amount: this.dinero.divide(divisor).getAmount(),
2025-02-24 19:00:28 +00:00
scale: this.scale,
currency_code: this.currency,
});
}
equalsTo(comparator: MoneyValue): boolean {
2025-02-25 10:16:34 +00:00
return this.dinero.equalsTo(comparator.dinero);
2025-02-24 19:00:28 +00:00
}
greaterThan(comparator: MoneyValue): boolean {
2025-02-25 10:16:34 +00:00
return this.dinero.greaterThan(comparator.dinero);
2025-02-24 19:00:28 +00:00
}
lessThan(comparator: MoneyValue): boolean {
2025-02-25 10:16:34 +00:00
return this.dinero.lessThan(comparator.dinero);
2025-02-24 19:00:28 +00:00
}
isZero(): boolean {
2025-02-25 10:16:34 +00:00
return this.dinero.isZero();
2025-02-24 19:00:28 +00:00
}
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);
}
}