Details
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/tree/ItemsInterval.java b/invoice/src/main/java/org/killbill/billing/invoice/tree/ItemsInterval.java
index 672045b..ff15213 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/tree/ItemsInterval.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/tree/ItemsInterval.java
@@ -17,17 +17,16 @@
package org.killbill.billing.invoice.tree;
import java.math.BigDecimal;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashSet;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
-import java.util.ListIterator;
-import java.util.Set;
+import java.util.Map;
import java.util.UUID;
import org.joda.time.LocalDate;
-
import org.killbill.billing.invoice.tree.Item.ItemAction;
import com.google.common.base.Preconditions;
@@ -92,68 +91,83 @@ public class ItemsInterval {
* @param mergeMode
* @return whether or not the parent should ignore the interval covered by the child interval
*/
- public boolean buildFromItems(final List<Item> output, final boolean mergeMode) {
- final ItemWithResult itemWithResult = getResultingItem(mergeMode);
- if (itemWithResult.getItem() != null) {
- output.add(itemWithResult.getItem());
+ public void buildFromItems(final List<Item> output, final boolean mergeMode) {
+ final Item item = getResultingItem(mergeMode);
+ if (item != null) {
+ output.add(item);
+ }
+ }
+
+ /**
+ * Remove all the cancelling pairs (ADD/CANCEL) for which CANCEL linkedId points to ADD id.
+ *
+ * @return true if there is no more items
+ */
+ public boolean mergeCancellingPairs() {
+
+ final Map<UUID, List<Item>> tmp = new HashMap<UUID, List<Item>>();
+ for (Item cur : items) {
+ final UUID idToConsider = (cur.getAction() == ItemAction.ADD) ? cur.getId() : cur.getLinkedId();
+ List<Item> listForItem = tmp.get(idToConsider);
+ if (listForItem == null) {
+ listForItem = new ArrayList<Item>(2);
+ tmp.put(idToConsider, listForItem);
+ }
+ listForItem.add(cur);
+ }
+
+ for (List<Item> listForIds : tmp.values()) {
+ if (listForIds.size() == 2) {
+ items.remove(listForIds.get(0));
+ items.remove(listForIds.get(1));
+ }
}
- return itemWithResult.isIgnorePeriod();
+ return items.size() == 0;
}
- private ItemWithResult getResultingItem(final boolean mergeMode) {
+ public Iterable<Item> get_ADD_items() {
+ return Iterables.filter(items, new Predicate<Item>() {
+ @Override
+ public boolean apply(final Item input) {
+ return input.getAction() == ItemAction.ADD;
+ }
+ });
+ }
+
+ public NodeInterval getNodeInterval() {
+ return interval;
+ }
+
+ private Item getResultingItem(final boolean mergeMode) {
return mergeMode ? getResulting_CANCEL_Item() : getResulting_ADD_Item();
}
- private ItemWithResult getResulting_CANCEL_Item() {
+ private Item getResulting_CANCEL_Item() {
Preconditions.checkState(items.size() == 0 || items.size() == 1);
- return new ItemWithResult(Iterables.tryFind(items, new Predicate<Item>() {
+ return Iterables.tryFind(items, new Predicate<Item>() {
@Override
public boolean apply(final Item input) {
return input.getAction() == ItemAction.CANCEL;
}
- }).orNull(), true);
+ }).orNull();
}
-
- private ItemWithResult getResulting_ADD_Item() {
-
- final Set<UUID> repairedIds = new HashSet<UUID>();
- final ListIterator<Item> it = items.listIterator(items.size());
+ private Item getResulting_ADD_Item() {
//
- // We can have an {0,n} pairs of ADD/CANCEL (cancelling each other), and in addition to that we could have:
- // a - One ADD that has not been cancelled => We want to return that item and let the parent (NodeInterval) know that it should ignore
- // the period as this is accounted for with the item returned
- // b - One CANCEL => We return NO item but we also want the parent to know that it should ignore
- // the period as this is accounted -- period should remain unbilled.
- // c - nothing => The parent should NOT ignore the period as there is no child element that can account for it.
+ // At this point we pruned the items so that we can have either:
+ // - 2 items (ADD + CANCEL, where CANCEL does NOT point to ADD item-- otherwise this is a cancelling pair that
+ // would have been removed in mergeCancellingPairs logic)
+ // - 1 ADD item, simple enough we return it
+ // - 1 CANCEL, there is nothing to return but the period will be ignored by the parent
+ // - Nothing at all; this valid, this just means its original items got removed during mergeCancellingPairs logic,
+ // but its NodeInterval has children so it could not be deleted.
//
- while (it.hasPrevious()) {
- final Item cur = it.previous();
- switch (cur.getAction()) {
- case ADD:
- // If we found a CANCEL item pointing to that item then don't return it as it was repair (full repair scenario)
- if (!repairedIds.contains(cur.getId())) {
- // Case a
- return new ItemWithResult(cur, true);
- } else {
- // Remove from the list so we know if there is anything else (case b or c)
- repairedIds.remove(cur.getId());
- }
-
- case CANCEL:
- // In all cases populate the set with the id of target item being repaired
- if (cur.getLinkedId() != null) {
- repairedIds.add(cur.getLinkedId());
- }
- break;
- }
- }
- return repairedIds.size() > 0 ?
- new ItemWithResult(null, true) : /* case b */
- new ItemWithResult(null, false); /* case c */
- }
+ Preconditions.checkState(items.size() <= 2);
+ final Item item = items.size() > 0 && items.get(0).getAction() == ItemAction.ADD ? items.get(0) : null;
+ return item;
+ }
// Just ensure that ADD items precedes CANCEL items
public void insertSortedItem(final Item item) {
@@ -179,6 +193,24 @@ public class ItemsInterval {
items.clear();
}
+ public void remove(final Item item) {
+ items.remove(item);
+ }
+
+ public Item getCancelledItemIfExists(final UUID targetId) {
+ final Item item = Iterables.tryFind(items, new Predicate<Item>() {
+ @Override
+ public boolean apply(final Item input) {
+ return input.getAction() == ItemAction.CANCEL && input.getLinkedId().equals(targetId);
+ }
+ }).orNull();
+ return item;
+ }
+
+ public int size() {
+ return items.size();
+ }
+
/**
* Creates a new item.
* <p/>
@@ -195,8 +227,7 @@ public class ItemsInterval {
*/
private Item createNewItem(LocalDate startDate, LocalDate endDate, final boolean mergeMode) {
- final ItemWithResult itemWithResult = getResultingItem(mergeMode);
- final Item item = itemWithResult.getItem();
+ final Item item = getResultingItem(mergeMode);
if (item == null) {
return null;
}
@@ -208,19 +239,4 @@ public class ItemsInterval {
return result;
}
- private final class ItemWithResult {
- private final Item item;
- private final boolean ignorePeriod;
-
- private ItemWithResult(final Item item, final boolean ignorePeriod) {
- this.item = item;
- this.ignorePeriod = ignorePeriod;
- }
- public Item getItem() {
- return item;
- }
- public boolean isIgnorePeriod() {
- return ignorePeriod;
- }
- }
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/tree/ItemsNodeInterval.java b/invoice/src/main/java/org/killbill/billing/invoice/tree/ItemsNodeInterval.java
index a43de4b..c215a5c 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/tree/ItemsNodeInterval.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/tree/ItemsNodeInterval.java
@@ -19,14 +19,15 @@ package org.killbill.billing.invoice.tree;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import org.joda.time.LocalDate;
-
import org.killbill.billing.util.jackson.ObjectMapper;
-import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonGenerator;
import com.google.common.base.Preconditions;
@@ -75,6 +76,10 @@ public class ItemsNodeInterval extends NodeInterval {
* @param output result list of built items
*/
public void buildForExistingItems(final List<Item> output) {
+
+ // We start by pruning useless entries to simplify the build phase.
+ pruneTree();
+
build(new BuildNodeCallback() {
@Override
public void onMissingInterval(final NodeInterval curNode, final LocalDate startDate, final LocalDate endDate) {
@@ -83,13 +88,14 @@ public class ItemsNodeInterval extends NodeInterval {
}
@Override
- public boolean onLastNode(final NodeInterval curNode) {
+ public void onLastNode(final NodeInterval curNode) {
final ItemsInterval items = ((ItemsNodeInterval) curNode).getItemsInterval();
- return items.buildFromItems(output, false);
+ items.buildFromItems(output, false);
}
});
}
+
/**
* The merge tree is initially constructed by flattening all the existing items and reversing them (CANCEL node).
* <p/>
@@ -113,19 +119,21 @@ public class ItemsNodeInterval extends NodeInterval {
* @param output result list of built items
*/
public void mergeExistingAndProposed(final List<Item> output) {
- build(new BuildNodeCallback() {
- @Override
- public void onMissingInterval(final NodeInterval curNode, final LocalDate startDate, final LocalDate endDate) {
- final ItemsInterval items = ((ItemsNodeInterval) curNode).getItemsInterval();
- items.buildForMissingInterval(startDate, endDate, output, true);
- }
- @Override
- public boolean onLastNode(final NodeInterval curNode) {
- final ItemsInterval items = ((ItemsNodeInterval) curNode).getItemsInterval();
- return items.buildFromItems(output, true);
- }
- });
+ build(new BuildNodeCallback() {
+ @Override
+ public void onMissingInterval(final NodeInterval curNode, final LocalDate startDate, final LocalDate endDate) {
+ final ItemsInterval items = ((ItemsNodeInterval) curNode).getItemsInterval();
+ items.buildForMissingInterval(startDate, endDate, output, true);
+ }
+
+ @Override
+ public void onLastNode(final NodeInterval curNode) {
+ final ItemsInterval items = ((ItemsNodeInterval) curNode).getItemsInterval();
+ items.buildFromItems(output, true);
+ }
+ }
+ );
}
/**
@@ -160,7 +168,7 @@ public class ItemsNodeInterval extends NodeInterval {
*
* @param newNode a new proposed item
* @return true if the item was merged and will trigger a repair or false if the proposed item should be kept as such
- * and no repair generated.
+ * and no repair generated.
*/
public boolean addProposedItem(final ItemsNodeInterval newNode) {
@@ -262,4 +270,66 @@ public class ItemsNodeInterval extends NodeInterval {
items.setAdjustment(amount, linkedId);
}
+ //
+ // Before we build the tree, we make a first pass at removing full repaired items; those can come in two shapes:
+ // Case A - The first one, is the mergeCancellingPairs logics which simply look for one CANCEL pointing to one ADD item in the same
+ // NodeInterval; this is fairly simple, and *only* requires removing those items and remove the interval from the tree when
+ // it has no more leaves and no more items.
+ // Case B - This is a bit more involved: We look for full repair that happened in pieces; this will translate to an ADD element of a NodeInterval,
+ // whose children completely map the interval (isPartitionedByChildren) and where each child will have a CANCEL item pointing to the ADD.
+ // When we detect such nodes, we delete both the ADD in the parent interval and the CANCEL in the children
+ //
+ private void pruneTree() {
+ walkTree(new WalkCallback() {
+ @Override
+ public void onCurrentNode(final int depth, final NodeInterval curNode, final NodeInterval parent) {
+
+ if(curNode.isRoot()) {
+ return;
+ }
+
+ final ItemsInterval curNodeItems = ((ItemsNodeInterval) curNode).getItemsInterval();
+ // Case A:
+ final boolean isEmpty = curNodeItems.mergeCancellingPairs();
+ if (isEmpty && curNode.getLeftChild() == null) {
+ curNode.getParent().removeChild(curNode);
+ }
+
+ if (!curNode.isPartitionedByChildren()) {
+ return;
+ }
+
+ // Case B -- look for such case, and if found (foundFullRepairByParts) we fix them below.
+ final Iterator<Item> it = curNodeItems.get_ADD_items().iterator();
+ while (it.hasNext()) {
+
+ final Item curAddItem = it.next();
+
+ NodeInterval curChild = curNode.getLeftChild();
+ Map<ItemsInterval, Item> toBeRemoved = new HashMap<ItemsInterval, Item>();
+ boolean foundFullRepairByParts = true;
+ while (curChild != null) {
+ final ItemsInterval curChildItems = ((ItemsNodeInterval) curChild).getItemsInterval();
+ Item cancellingItem = curChildItems.getCancelledItemIfExists(curAddItem.getId());
+ if (cancellingItem == null) {
+ foundFullRepairByParts = false;
+ break;
+ }
+ toBeRemoved.put(curChildItems, cancellingItem);
+ curChild = curChild.getRightSibling();
+ }
+
+ if (foundFullRepairByParts) {
+ for (ItemsInterval curItemsInterval : toBeRemoved.keySet()) {
+ curItemsInterval.remove(toBeRemoved.get(curItemsInterval));
+ if (curItemsInterval.size() == 0) {
+ curNode.removeChild(curItemsInterval.getNodeInterval());
+ }
+ }
+ curNodeItems.remove(curAddItem);
+ }
+ }
+ }
+ });
+ }
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/tree/NodeInterval.java b/invoice/src/main/java/org/killbill/billing/invoice/tree/NodeInterval.java
index f96b256..884e31d 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/tree/NodeInterval.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/tree/NodeInterval.java
@@ -51,12 +51,13 @@ public class NodeInterval {
* @param callback the callback which perform the build logic.
* @return whether or not the parent NodeInterval should ignore the period covered by the child (NodeInterval)
*/
- public boolean build(final BuildNodeCallback callback) {
+ public void build(final BuildNodeCallback callback) {
Preconditions.checkNotNull(callback);
if (leftChild == null) {
- return callback.onLastNode(this);
+ callback.onLastNode(this);
+ return;
}
LocalDate curDate = start;
@@ -65,10 +66,9 @@ public class NodeInterval {
if (curChild.getStart().compareTo(curDate) > 0) {
callback.onMissingInterval(this, curDate, curChild.getStart());
}
- boolean ignorePeriod = curChild.build(callback);
- if (ignorePeriod) {
- curDate = curChild.getEnd();
- }
+ curChild.build(callback);
+ // Note that skip to child endDate, meaning that we always consider the child [start end]
+ curDate = curChild.getEnd();
curChild = curChild.getRightSibling();
}
@@ -76,7 +76,7 @@ public class NodeInterval {
if (curDate.compareTo(end) < 0) {
callback.onMissingInterval(this, curDate, end);
}
- return true;
+ return;
}
/**
@@ -85,7 +85,7 @@ public class NodeInterval {
* @param newNode the node to be added
* @param callback the callback that will allow to specify insertion and return behavior.
* @return true if node was inserted. Note that this is driven by the callback, this method is generic
- * and specific behavior can be tuned through specific callbacks.
+ * and specific behavior can be tuned through specific callbacks.
*/
public boolean addNode(final NodeInterval newNode, final AddNodeCallback callback) {
@@ -149,6 +149,44 @@ public class NodeInterval {
}
}
+ public void removeChild(final NodeInterval toBeRemoved) {
+
+ NodeInterval prevChild = null;
+ NodeInterval curChild = leftChild;
+ while (curChild != null) {
+ if (curChild.isSame(toBeRemoved)) {
+ if (prevChild == null) {
+ leftChild = curChild.getRightSibling();
+ } else {
+ prevChild.rightSibling = curChild.getRightSibling();
+ }
+ break;
+ }
+ prevChild = curChild;
+ curChild = curChild.getRightSibling();
+ }
+
+ }
+
+ @JsonIgnore
+ public boolean isPartitionedByChildren() {
+
+ if (leftChild == null) {
+ return false;
+ }
+
+ LocalDate curDate = start;
+ NodeInterval curChild = leftChild;
+ while (curChild != null) {
+ if (curChild.getStart().compareTo(curDate) > 0) {
+ return false;
+ }
+ curDate = curChild.getEnd();
+ curChild = curChild.getRightSibling();
+ }
+ return (curDate.compareTo(end) == 0);
+ }
+
/**
* Return the first node satisfying the date and match callback.
*
@@ -227,7 +265,6 @@ public class NodeInterval {
}
}
-
public boolean isItemContained(final NodeInterval newNode) {
return (newNode.getStart().compareTo(start) >= 0 &&
newNode.getStart().compareTo(end) <= 0 &&
@@ -243,6 +280,13 @@ public class NodeInterval {
}
@JsonIgnore
+ public boolean isSame(final NodeInterval otherNode) {
+ return ((otherNode.getStart().compareTo(start) == 0 &&
+ otherNode.getEnd().compareTo(end) == 0) &&
+ otherNode.getParent().equals(parent));
+ }
+
+ @JsonIgnore
public boolean isRoot() {
return parent == null;
}
@@ -338,6 +382,7 @@ public class NodeInterval {
* Provides callback for walking the tree.
*/
public interface WalkCallback {
+
public void onCurrentNode(final int depth, final NodeInterval curNode, final NodeInterval parent);
}
@@ -345,6 +390,7 @@ public class NodeInterval {
* Provides custom logic for the search.
*/
public interface SearchCallback {
+
/**
* Custom logic to decide which node to return.
*
@@ -372,10 +418,8 @@ public class NodeInterval {
* Called when we hit a node with no children
*
* @param curNode current node
- * @return true if the curNode's parent should ignore that interval -- accounted for by curChild.
- *
*/
- public boolean onLastNode(NodeInterval curNode);
+ public void onLastNode(NodeInterval curNode);
}
/**
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
index 9b35daf..9008613 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
@@ -47,6 +47,7 @@ import org.killbill.billing.invoice.MockBillingEventSet;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.api.InvoicePaymentType;
import org.killbill.billing.invoice.model.DefaultInvoice;
import org.killbill.billing.invoice.model.DefaultInvoicePayment;
@@ -913,7 +914,9 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final MockPlanPhase phase = new MockPlanPhase(recurringPrice, null);
final Plan plan = new MockPlan(phase);
- final Invoice existingInvoice = new DefaultInvoice(UUID.randomUUID(), accountId, null, clock.getUTCToday(), new LocalDate(2013, 7, 15), currency, false);
+ final LocalDate targetDate = new LocalDate(2013, 10, 30);
+
+ final Invoice existingInvoice = new DefaultInvoice(UUID.randomUUID(), accountId, null, clock.getUTCToday(), targetDate, currency, false);
// Set the existing recurring invoice item 2013/06/15 - 2013/07/15
final LocalDate startDate = new LocalDate(2013, 06, 15);
@@ -933,6 +936,9 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
existingInvoice.addInvoiceItem(repairItem);
// Create the billing event associated with the subscription creation
+ //
+ // Note : this is the interesting part of the test; it does not provide the blocking billing events, which force invoice
+ // to un repair what was previously repaired.
final BillingEventSet events = new MockBillingEventSet();
final BillingEvent event = invoiceUtil.createMockBillingEvent(null, subscription, new DateTime("2013-06-15", DateTimeZone.UTC),
plan, phase,
@@ -945,20 +951,34 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
existingInvoices.add(existingInvoice);
// Generate a new invoice
- final Invoice invoice = generator.generateInvoice(accountId, events, existingInvoices, new LocalDate(2013, 10, 30), currency, internalCallContext);
+
+ final Invoice invoice = generator.generateInvoice(accountId, events, existingInvoices, targetDate, currency, internalCallContext);
assertEquals(invoice.getNumberOfItems(), 7);
+ assertEquals(invoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
assertEquals(invoice.getInvoiceItems().get(0).getStartDate(), new LocalDate(2013, 6, 15));
assertEquals(invoice.getInvoiceItems().get(0).getEndDate(), new LocalDate(2013, 7, 15));
+
+ assertEquals(invoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
assertEquals(invoice.getInvoiceItems().get(1).getStartDate(), new LocalDate(2013, 6, 15));
assertEquals(invoice.getInvoiceItems().get(1).getEndDate(), new LocalDate(2013, 6, 21));
+
+ assertEquals(invoice.getInvoiceItems().get(2).getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
assertEquals(invoice.getInvoiceItems().get(2).getStartDate(), new LocalDate(2013, 6, 26));
assertEquals(invoice.getInvoiceItems().get(2).getEndDate(), new LocalDate(2013, 7, 15));
+
+ assertEquals(invoice.getInvoiceItems().get(3).getInvoiceItemType(), InvoiceItemType.RECURRING);
assertEquals(invoice.getInvoiceItems().get(3).getStartDate(), new LocalDate(2013, 7, 15));
assertEquals(invoice.getInvoiceItems().get(3).getEndDate(), new LocalDate(2013, 8, 15));
+
+ assertEquals(invoice.getInvoiceItems().get(4).getInvoiceItemType(), InvoiceItemType.RECURRING);
assertEquals(invoice.getInvoiceItems().get(4).getStartDate(), new LocalDate(2013, 8, 15));
assertEquals(invoice.getInvoiceItems().get(4).getEndDate(), new LocalDate(2013, 9, 15));
+
+ assertEquals(invoice.getInvoiceItems().get(5).getInvoiceItemType(), InvoiceItemType.RECURRING);
assertEquals(invoice.getInvoiceItems().get(5).getStartDate(), new LocalDate(2013, 9, 15));
assertEquals(invoice.getInvoiceItems().get(5).getEndDate(), new LocalDate(2013, 10, 15));
+
+ assertEquals(invoice.getInvoiceItems().get(6).getInvoiceItemType(), InvoiceItemType.RECURRING);
assertEquals(invoice.getInvoiceItems().get(6).getStartDate(), new LocalDate(2013, 10, 15));
assertEquals(invoice.getInvoiceItems().get(6).getEndDate(), new LocalDate(2013, 11, 15));
@@ -966,10 +986,8 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
existingInvoices.add(invoice);
// Generate next invoice (no-op)
- final Invoice newInvoice = generator.generateInvoice(accountId, events, existingInvoices, new LocalDate(2013, 10, 30), currency, internalCallContext);
- assertEquals(newInvoice.getNumberOfItems(), 1);
- assertEquals(invoice.getInvoiceItems().get(0).getStartDate(), new LocalDate(2013, 6, 15));
- assertEquals(invoice.getInvoiceItems().get(0).getEndDate(), new LocalDate(2013, 7, 15));
+ final Invoice newInvoice = generator.generateInvoice(accountId, events, existingInvoices, targetDate, currency, internalCallContext);
+ assertNull(newInvoice);
}
private void distributeItems(final List<Invoice> invoices) {
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/tree/TestNodeInterval.java b/invoice/src/test/java/org/killbill/billing/invoice/tree/TestNodeInterval.java
index bbfeafd..ba35fff 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/tree/TestNodeInterval.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/tree/TestNodeInterval.java
@@ -160,9 +160,9 @@ public class TestNodeInterval /* extends InvoiceTestSuiteNoDB */ {
}
@Override
- public boolean onLastNode(final NodeInterval curNode) {
+ public void onLastNode(final NodeInterval curNode) {
// Nothing
- return true;
+ return;
}
});
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/tree/TestSubscriptionItemTree.java b/invoice/src/test/java/org/killbill/billing/invoice/tree/TestSubscriptionItemTree.java
index b02c83b..1a12fdb 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/tree/TestSubscriptionItemTree.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/tree/TestSubscriptionItemTree.java
@@ -218,7 +218,6 @@ public class TestSubscriptionItemTree /* extends InvoiceTestSuiteNoDB */ {
expectedResult.add(expected1);
expectedResult.add(expected2);
- // First test with items in order
SubscriptionItemTree tree = new SubscriptionItemTree(subscriptionId);
tree.addItem(first);
tree.addItem(second);