/*
* Copyright 2011-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package pl.com.bottega.ecommerce.sharedkernel;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Currency;
import javax.persistence.Embeddable;
import org.fest.util.Objects;
import pl.com.bottega.ddd.annotations.domain.ValueObject;
/**
*
*/
@SuppressWarnings("serial")
@Embeddable
@ValueObject
public class Money implements Serializable {
public static final Currency DEFAULT_CURRENCY = Currency.getInstance("EUR");
public static final Money ZERO = new Money(BigDecimal.ZERO);
private BigDecimal denomination;
private String currencyCode;
protected Money() {
}
public Money(BigDecimal denomination, Currency currency) {
this(denomination, currency.getCurrencyCode());
}
private Money(BigDecimal denomination, String currencyCode) {
this.denomination = denomination.setScale(2, RoundingMode.HALF_EVEN);
this.currencyCode = currencyCode;
}
public Money(BigDecimal denomination) {
this(denomination, DEFAULT_CURRENCY);
}
public Money(double denomination, Currency currency) {
this(new BigDecimal(denomination), currency.getCurrencyCode());
}
public Money(double denomination, String currencyCode) {
this(new BigDecimal(denomination), currencyCode);
}
public Money(double denomination) {
this(denomination, DEFAULT_CURRENCY);
}
public Money multiplyBy(double multiplier) {
return multiplyBy(new BigDecimal(multiplier));
}
public Money multiplyBy(BigDecimal multiplier) {
return new Money(denomination.multiply(multiplier), currencyCode);
}
public Money add(Money money) {
if (!compatibleCurrency(money)) {
throw new IllegalArgumentException("Currency mismatch");
}
return new Money(denomination.add(money.denomination), determineCurrencyCode(money));
}
public Money subtract(Money money) {
if (!compatibleCurrency(money))
throw new IllegalArgumentException("Currency mismatch");
return new Money(denomination.subtract(money.denomination), determineCurrencyCode(money));
}
/**
* Currency is compatible if the same or either money object has zero value.
*/
private boolean compatibleCurrency(Money money) {
return isZero(denomination) || isZero(money.denomination) || currencyCode.equals(money.getCurrencyCode());
}
private boolean isZero(BigDecimal testedValue) {
return BigDecimal.ZERO.compareTo(testedValue) == 0;
}
/**
* @return currency from this object or otherCurrencyCode. Preferred is the
* one that comes from Money that has non-zero value.
*/
private Currency determineCurrencyCode(Money otherMoney) {
String resultingCurrenctCode = isZero(denomination) ? otherMoney.currencyCode : currencyCode;
return Currency.getInstance(resultingCurrenctCode);
}
public String getCurrencyCode() {
return currencyCode;
}
public Currency getCurrency() {
return Currency.getInstance(currencyCode);
}
public boolean greaterThan(Money other) {
return denomination.compareTo(other.denomination) > 0;
}
public boolean lessThan(Money other) {
return denomination.compareTo(other.denomination) < 0;
}
public boolean lessOrEquals(Money other) {
return denomination.compareTo(other.denomination) <= 0;
}
@Override
public String toString() {
return String.format("%0$.2f %s", denomination, getCurrency().getSymbol());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((currencyCode == null) ? 0 : currencyCode.hashCode());
result = prime * result + ((denomination == null) ? 0 : denomination.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Money other = (Money) obj;
return compatibleCurrency(other) && Objects.areEqual(denomination, other.denomination);
}
}