/*
* Copyright 2010-2011 Ning, Inc.
*
* Ning licenses this file to you 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 com.ning.billing.invoice.model;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionTransition;
import com.ning.billing.invoice.api.InvoiceItem;
import org.joda.time.DateTime;
import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.util.UUID;
public class DefaultInvoiceItem implements InvoiceItem {
private final UUID id;
private final UUID invoiceId;
private final UUID subscriptionId;
private final String planName;
private final String phaseName;
private DateTime startDate;
private DateTime endDate;
private BigDecimal recurringAmount;
private final BigDecimal recurringRate;
private final BigDecimal fixedAmount;
private final Currency currency;
public DefaultInvoiceItem(UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
DateTime startDate, DateTime endDate,
BigDecimal recurringAmount, BigDecimal recurringRate,
BigDecimal fixedAmount, Currency currency) {
this(UUID.randomUUID(), invoiceId, subscriptionId, planName, phaseName, startDate, endDate,
recurringAmount, recurringRate, fixedAmount, currency);
}
public DefaultInvoiceItem(UUID id, UUID invoiceId, UUID subscriptionId, String planName, String phaseName,
DateTime startDate, DateTime endDate,
BigDecimal recurringAmount, BigDecimal recurringRate,
BigDecimal fixedAmount, Currency currency) {
this.id = id;
this.invoiceId = invoiceId;
this.subscriptionId = subscriptionId;
this.planName = planName;
this.phaseName = phaseName;
this.startDate = startDate;
this.endDate = endDate;
this.recurringAmount = recurringAmount;
this.recurringRate = recurringRate;
this.fixedAmount = fixedAmount;
this.currency = currency;
}
public DefaultInvoiceItem(InvoiceItem that, UUID invoiceId) {
this.id = UUID.randomUUID();
this.invoiceId = invoiceId;
this.subscriptionId = that.getSubscriptionId();
this.planName = that.getPlanName();
this.phaseName = that.getPhaseName();
this.startDate = that.getStartDate();
this.endDate = that.getEndDate();
this.recurringAmount = that.getRecurringAmount();
this.recurringRate = that.getRecurringRate();
this.fixedAmount = that.getFixedAmount();
this.currency = that.getCurrency();
}
@Override
public InvoiceItem asCredit(UUID invoiceId) {
BigDecimal recurringAmountNegated = recurringAmount == null ? null : recurringAmount.negate();
BigDecimal fixedAmountNegated = fixedAmount == null ? null : fixedAmount.negate();
return new DefaultInvoiceItem(invoiceId, subscriptionId, planName, phaseName, startDate, endDate,
recurringAmountNegated, recurringRate, fixedAmountNegated, currency);
}
@Override
public UUID getId() {
return id;
}
@Override
public UUID getInvoiceId() {
return invoiceId;
}
@Override
public UUID getSubscriptionId() {
return subscriptionId;
}
@Override
public String getPlanName() {
return planName;
}
@Override
public String getPhaseName() {
return phaseName;
}
@Override
public DateTime getStartDate() {
return startDate;
}
@Override
public DateTime getEndDate() {
return endDate;
}
@Override
public BigDecimal getRecurringAmount() {
return recurringAmount;
}
@Override
public BigDecimal getRecurringRate() {
return recurringRate;
}
@Override
public BigDecimal getFixedAmount() {
return fixedAmount;
}
@Override
public Currency getCurrency() {
return currency;
}
@Override
public int compareTo(InvoiceItem that) {
int compareSubscriptions = getSubscriptionId().compareTo(that.getSubscriptionId());
if (compareSubscriptions == 0) {
// move null end dates to the end of the set
if ((this.endDate != null) && (that.getEndDate() == null)) {
return -1;
}
if ((this.endDate == null) && (that.getEndDate() != null)) {
return 1;
}
int compareStartDates = getStartDate().compareTo(that.getStartDate());
return compareStartDates;
} else {
return compareSubscriptions;
}
}
// TODO: deal with error cases
@Override
public void subtract(InvoiceItem that) {
if (this.startDate.equals(that.getStartDate()) && this.endDate.equals(that.getEndDate())) {
this.startDate = this.endDate;
this.recurringAmount = safeSubtract(this.recurringAmount, that.getRecurringAmount());
} else {
if (this.startDate.equals(that.getStartDate())) {
this.startDate = that.getEndDate();
this.recurringAmount = safeSubtract(this.recurringAmount, that.getRecurringAmount());
}
if (this.endDate.equals(that.getEndDate())) {
this.endDate = that.getStartDate();
this.recurringAmount = safeSubtract(this.recurringAmount, that.getRecurringAmount());
}
}
}
private BigDecimal safeSubtract(BigDecimal minuend, BigDecimal subtrahend) {
// minuend - subtrahend == difference
if (minuend == null) {
if (subtrahend == null) {
return BigDecimal.ZERO;
} else {
return subtrahend.negate();
}
} else {
if (subtrahend == null) {
return minuend;
} else {
return minuend.subtract(subtrahend);
}
}
}
@Override
public boolean duplicates(InvoiceItem that) {
if (!this.getSubscriptionId().equals(that.getSubscriptionId())) {return false;}
if (!this.planName.equals(that.getPlanName())) {return false;}
if (!this.phaseName.equals(that.getPhaseName())) {return false;}
if (!compareNullableBigDecimal(this.getRecurringRate(), that.getRecurringRate())) {return false;}
if (!this.getCurrency().equals(that.getCurrency())) {return false;}
if ((this.endDate == null) && (that.getEndDate() == null) && (this.startDate.compareTo(that.getStartDate()) == 0)) {
return true;
}
DateRange thisDateRange = new DateRange(this.getStartDate(), this.getEndDate());
return thisDateRange.contains(that.getStartDate()) && (that.getEndDate() == null || thisDateRange.contains(that.getEndDate()));
}
private boolean compareNullableBigDecimal(@Nullable BigDecimal value1, @Nullable BigDecimal value2) {
if ((value1 == null) && (value2 != null)) {return false;}
if ((value1 != null) && (value2 == null)) {return false;}
if ((value1 != null) && (value2 != null)) {
if (value1.compareTo(value2) != 0) {return false;}
}
return true;
}
/**
* indicates whether the supplied item is a cancelling item for this item
* @param that the InvoiceItem to be examined
* @return true if the two invoice items cancel each other out (same subscription, same date range, sum of amounts = 0)
*/
@Override
public boolean cancels(InvoiceItem that) {
if(!this.getSubscriptionId().equals(that.getSubscriptionId())) {return false;}
if(!this.getEndDate().equals(that.getEndDate())) {return false;}
if(!this.getStartDate().equals(that.getStartDate())) {return false;}
if (!safeCheckForZeroSum(this.getRecurringAmount(), that.getRecurringAmount())) {return false;}
if (!safeCheckForEquality(this.getRecurringRate(), that.getRecurringRate())) {return false;}
if (!safeCheckForZeroSum(this.getFixedAmount(), that.getFixedAmount())) {return false;}
if(!this.getCurrency().equals(that.getCurrency())) {return false;}
return true;
}
private boolean safeCheckForZeroSum(final BigDecimal value1, final BigDecimal value2) {
if ((value1 == null) && (value2 == null)) {return true;}
if ((value1 == null) ^ (value2 == null)) {return false;}
return (value1.add(value2).compareTo(BigDecimal.ZERO) == 0);
}
private boolean safeCheckForEquality(final BigDecimal value1, final BigDecimal value2) {
if ((value1 == null) && (value2 == null)) {return true;}
if ((value1 == null) ^ (value2 == null)) {return false;}
return (value1.compareTo(value2) == 0);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("InvoiceItem = {").append("id = ").append(id.toString()).append(", ");
sb.append("invoiceId = ").append(invoiceId.toString()).append(", ");
sb.append("subscriptionId = ").append(subscriptionId.toString()).append(", ");
sb.append("planName = ").append(planName).append(", ");
sb.append("phaseName = ").append(phaseName).append(", ");
sb.append("startDate = ").append(startDate.toString()).append(", ");
if (endDate != null) {
sb.append("endDate = ").append(endDate.toString()).append(", ");
} else {
sb.append("endDate = null");
}
sb.append("recurringAmount = ");
if (recurringAmount == null) {
sb.append("null");
} else {
sb.append(recurringAmount.toString());
}
sb.append(", ");
sb.append("recurringRate = ");
if (recurringRate == null) {
sb.append("null");
} else {
sb.append(recurringRate.toString());
}
sb.append(", ");
sb.append("fixedAmount = ");
if (fixedAmount == null) {
sb.append("null");
} else {
sb.append(fixedAmount.toString());
}
sb.append("}");
return sb.toString();
}
}