Money.java

package com.github.jenkaby.bikerental.shared.domain.model.vo;

import java.math.BigDecimal;
import java.math.RoundingMode;

public record Money(BigDecimal amount) implements Comparable<Money> {
    private static final int MONEY_SCALE = 2;

    private static final RoundingMode MONEY_ROUNDING_MODE = RoundingMode.HALF_UP;
    public Money {
        if (amount == null) {
            throw new IllegalArgumentException("Amount cannot be null");
        }
        if (amount.scale() > MONEY_SCALE) {
            throw new IllegalArgumentException("Amount cannot have more than " + MONEY_SCALE + " decimal places");
        }
    }

    /**
     * Creates Money from BigDecimal, automatically rounding to 2 decimal places if needed.
     *
     * @param amount amount to create Money from
     * @return Money instance with rounded amount
     */
    public static Money of(BigDecimal amount) {
        if (amount == null) {
            throw new IllegalArgumentException("Amount cannot be null");
        }
        BigDecimal roundedAmount = amount.scale() > MONEY_SCALE
                ? amount.setScale(MONEY_SCALE, MONEY_ROUNDING_MODE)
                : amount;
        return new Money(roundedAmount);
    }

    public static Money of(String amount) {
        return Money.of(new BigDecimal(amount));
    }

    public static Money zero() {
        return new Money(BigDecimal.ZERO);
    }

    public boolean isPositive() {
        return amount.compareTo(BigDecimal.ZERO) > 0;
    }

    public boolean isNegative() {
        return amount.compareTo(BigDecimal.ZERO) < 0;
    }

    public boolean isZero() {
        return amount.compareTo(BigDecimal.ZERO) == 0;
    }

    public boolean isNegativeOrZero() {
        return amount.compareTo(BigDecimal.ZERO) <= 0;
    }

    public Money abs() {
        return new Money(amount.abs());
    }

    public boolean isMoreThan(Money other) {
        return this.compareTo(other) > 0;
    }

    public boolean isLessThan(Money other) {
        return this.compareTo(other) < 0;
    }

    public Money add(Money other) {
        return new Money(this.amount.add(other.amount));
    }

    public Money subtract(Money other) {
        return new Money(this.amount.subtract(other.amount));
    }

    public Money multiply(BigDecimal multiplier) {
        return Money.of(this.amount.multiply(multiplier));
    }

    public Money divide(int divisor) {
        return Money.of(amount.divide(BigDecimal.valueOf(divisor), MONEY_SCALE, MONEY_ROUNDING_MODE));
    }

    @Override
    public String toString() {
        return amount.stripTrailingZeros().toPlainString();
    }

    @Override
    public int compareTo(Money o) {
        return this.amount.compareTo(o.amount);
    }
}