/*
* Copyright 2010-2014 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.tree;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.UUID;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import com.ning.billing.invoice.api.InvoiceItem;
import com.ning.billing.invoice.api.InvoiceItemType;
import com.ning.billing.invoice.generator.InvoiceDateUtils;
import com.ning.billing.invoice.model.InvoicingConfiguration;
import com.ning.billing.invoice.model.RecurringInvoiceItem;
import com.google.common.collect.Lists;
public class ItemsInterval {
private static final int ROUNDING_MODE = InvoicingConfiguration.getRoundingMode();
private static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
private LinkedList<InvoiceItem> items;
public ItemsInterval() {
this(null);
}
public ItemsInterval(final InvoiceItem initialItem) {
this.items = Lists.newLinkedList();
if (initialItem != null) {
items.add(initialItem);
}
}
public List<InvoiceItem> getItems() {
return items;
}
public void buildForNonRepairedItems(final LocalDate startDate, final LocalDate endDate, final List<InvoiceItem> output) {
final InvoiceItem item = createRecuringItem(startDate, endDate);
if (item != null) {
output.add(item);
}
}
public void buildFromItems(final List<InvoiceItem> output) {
final Set<UUID> repairedIds = new HashSet<UUID>();
ListIterator<InvoiceItem> it = items.listIterator(items.size());
while (it.hasPrevious()) {
final InvoiceItem cur = it.previous();
switch (cur.getInvoiceItemType()) {
case FIXED:
case RECURRING:
// The only time we could see that true is a case of full repair, when the repair
// points to an item that will end up in the same ItemsInterval
if (!repairedIds.contains(cur.getId())) {
output.add(cur);
}
break;
case REPAIR_ADJ:
repairedIds.add(cur.getLinkedItemId());
break;
case ITEM_ADJ:
// If item has been adjusted, we assume the item should not be re-invoiced so we leave it in the list.
break;
// Ignored
case EXTERNAL_CHARGE:
case CBA_ADJ:
case CREDIT_ADJ:
case REFUND_ADJ:
default:
}
}
}
public void insertSortedItem(final InvoiceItem item) {
items.add(item);
Collections.sort(items, new Comparator<InvoiceItem>() {
@Override
public int compare(final InvoiceItem o1, final InvoiceItem o2) {
final int type1 = o1.getInvoiceItemType().ordinal();
final int type2 = o2.getInvoiceItemType().ordinal();
return (type1 < type2) ? -1 : ((type1 == type2) ? 0 : 1);
}
});
}
private InvoiceItem createRecuringItem(LocalDate startDate, LocalDate endDate) {
final List<InvoiceItem> itemToConsider = new LinkedList<InvoiceItem>();
buildFromItems(itemToConsider);
Iterator<InvoiceItem> it = itemToConsider.iterator();
while (it.hasNext()) {
final InvoiceItem cur = it.next();
if (cur.getInvoiceItemType() == InvoiceItemType.RECURRING &&
cur.getStartDate().compareTo(startDate) <= 0 &&
cur.getEndDate().compareTo(endDate) >= 0) {
int nbTotalRepairedDays = Days.daysBetween(cur.getStartDate(), cur.getEndDate()).getDays();
final BigDecimal amount = InvoiceDateUtils.calculateProrationBetweenDates(startDate, endDate, nbTotalRepairedDays).multiply(cur.getRate()).setScale(NUMBER_OF_DECIMALS, ROUNDING_MODE);
return new RecurringInvoiceItem(cur.getInvoiceId(), cur.getAccountId(), cur.getBundleId(), cur.getSubscriptionId(),
cur.getPlanName(), cur.getPhaseName(), startDate, endDate, amount, cur.getRate(), cur.getCurrency());
}
}
return null;
}
}