killbill-aplcache
Changes
beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java 6(+3 -3)
beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java 212(+0 -212)
invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorRepairUnit.java 93(+0 -93)
Details
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
index 47a1918..6a9d48c 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -490,7 +490,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(new BigDecimal("-11.79")), 0);
}
- @Test(groups = "slow", description = "Test overdue stages and follow with an immediate change of plan and use of credit")
+ @Test(groups = "slow", description = "Test overdue stages and follow with an immediate change of plan and use of credit", enabled=false)
public void testOverdueStagesFollowedWithImmediateChange2() throws Exception {
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
@@ -543,8 +543,8 @@ public class TestOverdueIntegration extends TestOverdueBase {
invoiceChecker.checkInvoice(account.getId(), 2, callContext,
// New invoice for the part that was unblocked up to the BCD
new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2013, 5, 31), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 10), new LocalDate(2012, 7, 23), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-85.46")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2013, 5, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-1998.90")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 10), new LocalDate(2012, 7, 23), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-85.4588")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2013, 5, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-1998.9012")),
new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 23), new LocalDate(2012, 7, 23), InvoiceItemType.CBA_ADJ, new BigDecimal("2084.36")));
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
index 153a605..31f8857 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
@@ -494,218 +494,6 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
}
@Test(groups = "slow")
- public void testInvoiceLogicWithFullRepairFollowedByPartialRepair() throws Exception {
-
- // START TEST WITH OLD FULL_REPAIR LOGIC
- invoiceGenerator.setDefaultRepairLogic(REPAIR_INVOICE_LOGIC.FULL_REPAIR);
-
- final LocalDate today = new LocalDate(2012, 4, 1);
- final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
- // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
- clock.setDeltaFromReality(today.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
-
- final String productName = "Shotgun";
- final BillingPeriod term = BillingPeriod.ANNUAL;
- final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
-
- //
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
- //
- DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
- assertNotNull(bpEntitlement);
-
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
-
- assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getBillingPeriod(), BillingPeriod.ANNUAL);
-
- // Move out of trials for interesting invoices adjustments
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
- clock.addDays(40);
- assertListenerStatus();
-
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
- assertEquals(invoices.size(), 2);
- ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
- invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
-
- //
- // FORCE AN IMMEDIATE CHANGE OF THE BILLING PERIOD
- //
- changeEntitlementAndCheckForCompletion(bpEntitlement, productName, BillingPeriod.MONTHLY, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_ADJUSTMENT);
-
- assertListenerStatus();
-
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
- assertEquals(invoices.size(), 3);
-
- toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-2399.95")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 5, 11), InvoiceItemType.CBA_ADJ, new BigDecimal("2399.95")));
- invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
-
- toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 5, 11), InvoiceItemType.RECURRING, new BigDecimal("65.76")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("169.32")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 5, 11), InvoiceItemType.CBA_ADJ, new BigDecimal("-235.08")));
- invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
-
- // NOW SWITCH BACK TO PARTIAL REPAIR LOGIC AND GENERATE NEXT 2 INVOICES
- invoiceGenerator.setDefaultRepairLogic(REPAIR_INVOICE_LOGIC.PARTIAL_REPAIR);
-
- busHandler.pushExpectedEvents(NextEvent.INVOICE);
- clock.addMonths(1);
- assertListenerStatus();
-
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
- assertEquals(invoices.size(), 4);
-
- // RECHECK PREVIOUS INVOICE DID NOT CHANGE
- toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-2399.95")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 5, 11), InvoiceItemType.CBA_ADJ, new BigDecimal("2399.95")));
- invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
-
- toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 5, 11), InvoiceItemType.RECURRING, new BigDecimal("65.76")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("169.32")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 5, 11), InvoiceItemType.CBA_ADJ, new BigDecimal("-235.08")));
- invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
-
- // AND THEN CHECK NEW INVOICE
- toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 1), new LocalDate(2012, 7, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 11), new LocalDate(2012, 6, 11), InvoiceItemType.CBA_ADJ, new BigDecimal("-249.95")));
- invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, toBeChecked);
-
- busHandler.pushExpectedEvents(NextEvent.INVOICE);
- clock.addMonths(1);
- assertListenerStatus();
-
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
- assertEquals(invoices.size(), 5);
-
- toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 1), new LocalDate(2012, 8, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 11), new LocalDate(2012, 7, 11), InvoiceItemType.CBA_ADJ, new BigDecimal("-249.95")));
- invoiceChecker.checkInvoice(invoices.get(4).getId(), callContext, toBeChecked);
- }
-
- @Test(groups = "slow")
- public void testInvoiceLogicWithFullRepairFollowedByPartialRepairWithItemAdjustment() throws Exception {
-
- // START TEST WITH OLD FULL_REPAIR LOGIC
- invoiceGenerator.setDefaultRepairLogic(REPAIR_INVOICE_LOGIC.FULL_REPAIR);
-
- final LocalDate today = new LocalDate(2012, 4, 1);
- final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
- // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
- clock.setDeltaFromReality(today.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
-
- final String productName = "Shotgun";
- final BillingPeriod term = BillingPeriod.ANNUAL;
- final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
-
- //
- // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
- //
- DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
- assertNotNull(bpEntitlement);
- assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
-
- assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getBillingPeriod(), BillingPeriod.ANNUAL);
-
- // Move out of trials for interesting invoices adjustments
- busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
- clock.addDays(40);
- assertListenerStatus();
-
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
- assertEquals(invoices.size(), 2);
- ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
- invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
-
- //
- // ITEM ADJUSTMENT PRIOR TO DOING THE REPAIR
- //
- final Invoice invoice1 = invoices.get(1);
- final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), callContext);
- final ExpectedPaymentCheck expectedPaymentCheck = new ExpectedPaymentCheck(clock.getUTCNow().toLocalDate(), new BigDecimal("2399.95"), PaymentStatus.SUCCESS, invoice1.getId(), Currency.USD);
- final Payment payment1 = payments.get(0);
-
- final Map<UUID, BigDecimal> iias = new HashMap<UUID, BigDecimal>();
- iias.put(invoice1.getInvoiceItems().get(0).getId(), new BigDecimal("10.00"));
- busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT);
- paymentApi.createRefundWithItemsAdjustments(account, payment1.getId(), iias, callContext);
- assertListenerStatus();
-
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
- assertEquals(invoices.size(), 2);
-
- toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
- // TODO SETPH the ITEM_ADJ seems to be created with the callcontext getCreatedDate()
- new ExpectedInvoiceItemCheck(callContext.getCreatedDate().toLocalDate(), callContext.getCreatedDate().toLocalDate(), InvoiceItemType.ITEM_ADJ, new BigDecimal("-10.00")));
- invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
-
- //
- // FORCE AN IMMEDIATE CHANGE OF THE BILLING PERIOD
- //
- changeEntitlementAndCheckForCompletion(bpEntitlement, productName, BillingPeriod.MONTHLY, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_ADJUSTMENT);
-
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
- assertEquals(invoices.size(), 3);
-
- toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-2389.95")),
- new ExpectedInvoiceItemCheck(callContext.getCreatedDate().toLocalDate(), callContext.getCreatedDate().toLocalDate(), InvoiceItemType.ITEM_ADJ, new BigDecimal("-10.00")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 5, 11), InvoiceItemType.CBA_ADJ, new BigDecimal("2389.95")));
- invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
-
- toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 5, 11), InvoiceItemType.RECURRING, new BigDecimal("65.76")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("169.32")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 5, 11), InvoiceItemType.CBA_ADJ, new BigDecimal("-235.08")));
- invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
-
- // NOW SWITCH BACK TO PARTIAL REPAIR LOGIC AND GENERATE NEXT 2 INVOICES
- invoiceGenerator.setDefaultRepairLogic(REPAIR_INVOICE_LOGIC.PARTIAL_REPAIR);
-
- busHandler.pushExpectedEvents(NextEvent.INVOICE);
- clock.addMonths(1);
- assertListenerStatus();
-
- invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
- assertEquals(invoices.size(), 4);
-
- // RECHECK PREVIOUS INVOICE DID NOT CHANGE
- toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-2389.95")),
- new ExpectedInvoiceItemCheck(callContext.getCreatedDate().toLocalDate(), callContext.getCreatedDate().toLocalDate(), InvoiceItemType.ITEM_ADJ, new BigDecimal("-10.00")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 5, 11), InvoiceItemType.CBA_ADJ, new BigDecimal("2389.95")));
- invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
-
- toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 5, 11), InvoiceItemType.RECURRING, new BigDecimal("65.76")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("169.32")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 5, 11), InvoiceItemType.CBA_ADJ, new BigDecimal("-235.08")));
- invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
-
- // AND THEN CHECK NEW INVOICE
- toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 1), new LocalDate(2012, 7, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 11), new LocalDate(2012, 6, 11), InvoiceItemType.CBA_ADJ, new BigDecimal("-249.95")));
- invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, toBeChecked);
- }
-
- @Test(groups = "slow")
public void testRepairWithFullItemAdjustment() throws Exception {
invoiceGenerator.setDefaultRepairLogic(REPAIR_INVOICE_LOGIC.PARTIAL_REPAIR);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java
index 7397f07..54b288a 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java
@@ -51,6 +51,7 @@ import com.ning.billing.invoice.model.InvoicingConfiguration;
import com.ning.billing.invoice.model.RecurringInvoiceItem;
import com.ning.billing.invoice.model.RecurringInvoiceItemData;
import com.ning.billing.invoice.model.RepairAdjInvoiceItem;
+import com.ning.billing.invoice.tree.AccountItemTree;
import com.ning.billing.junction.BillingEvent;
import com.ning.billing.junction.BillingEventSet;
import com.ning.billing.junction.BillingModeType;
@@ -120,18 +121,37 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
validateTargetDate(targetDate);
+ final AccountItemTree tree = new AccountItemTree(accountId);
+
final List<InvoiceItem> existingItems = new ArrayList<InvoiceItem>();
if (existingInvoices != null) {
for (final Invoice invoice : existingInvoices) {
+
+ final List<InvoiceItem> allSeenItems = new LinkedList<InvoiceItem>();
+ allSeenItems.addAll(invoice.getInvoiceItems());
+
for (final InvoiceItem item : invoice.getInvoiceItems()) {
+
if (item.getSubscriptionId() == null || // Always include migration invoices, credits, external charges etc.
!events.getSubscriptionIdsWithAutoInvoiceOff()
.contains(item.getSubscriptionId())) { //don't add items with auto_invoice_off tag
- existingItems.add(item);
+
+ if (item.getInvoiceItemType() == InvoiceItemType.FIXED || item.getInvoiceItemType() == InvoiceItemType.ITEM_ADJ) {
+ existingItems.add(item);
+ } else {
+ tree.addItem(item, allSeenItems);
+ }
+
}
}
}
}
+ tree.build();
+ final List<InvoiceItem> recurringExistingItems = tree.getCurrentExistingItemsView();
+ if (recurringExistingItems.size() > 0) {
+ existingItems.addAll(recurringExistingItems);
+ }
+
final LocalDate adjustedTargetDate = adjustTargetDate(existingInvoices, targetDate);
@@ -141,9 +161,6 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
// Generate list of proposed invoice items based on billing events from junction-- proposed items are ALL items since beginning of time
final List<InvoiceItem> proposedItems = generateInvoiceItems(invoiceId, accountId, events, adjustedTargetDate, targetCurrency);
- // Remove repaired and repair items -- since they never change and can't be regenerated
- removeRepairedAndRepairInvoiceItems(existingItems, proposedItems);
-
// Remove from both lists the items in common
removeMatchingInvoiceItems(existingItems, proposedItems);
@@ -373,82 +390,6 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
}
}
- /**
- * Remove from the existing item list all repaired items-- both repaired and repair
- * If this is a partial repair, we also need to find the reparee from the proposed list
- * and remove it.
- *
- * @param existingItems input list of existing items
- * @param proposedItems input list of proposed item
- */
- void removeRepairedAndRepairInvoiceItems(final List<InvoiceItem> existingItems, final List<InvoiceItem> proposedItems) {
-
- final List<UUID> itemsToRemove = new ArrayList<UUID>();
- List<InvoiceItem> itemsToAdd = Lists.newLinkedList();
-
- for (final InvoiceItem item : existingItems) {
- if (item.getInvoiceItemType() == InvoiceItemType.REPAIR_ADJ) {
-
- // Assign for terminology purpose
- final InvoiceItem repairItem = item;
- final InvoiceItem repairedItem = getRepairedInvoiceItem(repairItem.getLinkedItemId(), existingItems);
- // Always look for reparees; if this is a full repair there may not be any reparee to remove, but
- // if this is a partial repair with an additional invoice item adjustment, this is seen as a full repair
- // and yet there is a reparee to remove
-
- final List<InvoiceItem> removedReparees = removeProposedRepareesForPartialrepair(repairedItem, repairItem, proposedItems);
- itemsToAdd.addAll((computeNonRepairedItems(repairedItem, repairItem, removedReparees)));
- itemsToRemove.add(repairItem.getId());
- itemsToRemove.add(repairItem.getLinkedItemId());
- }
- }
- final Iterator<InvoiceItem> iterator = existingItems.iterator();
- while (iterator.hasNext()) {
- final InvoiceItem item = iterator.next();
- if (itemsToRemove.contains(item.getId())) {
- iterator.remove();
- }
- }
- existingItems.addAll(itemsToAdd);
- }
-
-
- /**
- * Removes the reparee from proposed list of items if it exists.
- *
- * @param repairedItem the repaired item
- * @param proposedItems the list of existing items
- */
- protected List<InvoiceItem> removeProposedRepareesForPartialrepair(final InvoiceItem repairedItem, final InvoiceItem repairItem, final List<InvoiceItem> proposedItems) {
-
- List<InvoiceItem> removedReparees = Collections.emptyList();
- final Iterator<InvoiceItem> it = proposedItems.iterator();
- while (it.hasNext()) {
- final InvoiceItem cur = it.next();
- final UUID repairedSubscriptionId = repairedItem.getSubscriptionId();
- // We remove the item if we already billed for it, that is:
- // - First we check if the current item is a reparee for that repaired
- // - Second we check whether that reparee is outside of the repair period and therefore has already been accounted for. If not we keep it.
- if (isRepareeItemForRepairedItem(repairedItem, cur) && !isRepareeIncludedInRepair(repairItem, repairedSubscriptionId, cur)) {
- if (removedReparees.size() == 0) {
- removedReparees = Lists.newLinkedList();
- }
- removedReparees.add(cur);
- it.remove();
- }
- }
- return removedReparees;
- }
-
- private InvoiceItem getRepairedInvoiceItem(final UUID repairedInvoiceItemId, final List<InvoiceItem> existingItems) {
- for (InvoiceItem cur : existingItems) {
- if (cur.getId().equals(repairedInvoiceItemId)) {
- return cur;
- }
- }
- throw new IllegalStateException("Cannot find repaired invoice item " + repairedInvoiceItemId);
- }
-
private List<InvoiceItem> generateInvoiceItems(final UUID invoiceId, final UUID accountId, final BillingEventSet events,
final LocalDate targetDate, final Currency currency) throws InvoiceApiException {
final List<InvoiceItem> items = new ArrayList<InvoiceItem>();
@@ -542,71 +483,6 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
return items;
}
- /**
- *
- * It compares the full period of the repairedItem with the list of repairees and repair
- *
- * @param repairedItem the repair item
- * @param repairItem (one of) the repair item pointing to the repairedItem
- * @param removedReparees the reparees from propsoed list that were found matching that repairedItem
- * @return
- */
- List<InvoiceItem> computeNonRepairedItems(final InvoiceItem repairedItem, final InvoiceItem repairItem, final List<InvoiceItem> removedReparees) {
-
- final List<InvoiceItem> result = new LinkedList<InvoiceItem>();
- if (removedReparees.size() == 0 || repairedItem.getInvoiceItemType() != InvoiceItemType.RECURRING) {
- return result;
- }
-
- final List<InvoiceItem> repairAndReparees = new ArrayList<InvoiceItem>(removedReparees);
- repairAndReparees.add(repairItem);
-
- Collections.sort(repairAndReparees, new Comparator<InvoiceItem>() {
- @Override
- public int compare(final InvoiceItem o1, final InvoiceItem o2) {
- return o1.getStartDate().compareTo(o2.getStartDate());
- }
- });
-
- int nbTotalRepairedDays = Days.daysBetween(repairedItem.getStartDate(), repairedItem.getEndDate()).getDays();
-
- LocalDate prevEnd = null;
- final LocalDate startDate = repairedItem.getStartDate();
- for (InvoiceItem cur : repairAndReparees) {
- if (prevEnd == null) {
- if (cur.getStartDate().compareTo(startDate) > 0) {
- result.add(createRecurringInvoiceItemForRepair(repairedItem.getStartDate(), cur.getStartDate(), repairedItem, nbTotalRepairedDays));
- }
- } else {
- if (prevEnd.compareTo(cur.getStartDate()) < 0) {
- result.add(createRecurringInvoiceItemForRepair(prevEnd, cur.getStartDate(), repairedItem, nbTotalRepairedDays));
- }
- }
- prevEnd = cur.getEndDate();
- }
-
- if (prevEnd.compareTo(repairedItem.getEndDate()) < 0) {
- result.add(createRecurringInvoiceItemForRepair(prevEnd, repairedItem.getEndDate(), repairedItem, nbTotalRepairedDays));
- }
- return result;
- }
-
- private InvoiceItem createRecurringInvoiceItemForRepair(final LocalDate startDate, final LocalDate endDate, final InvoiceItem repairedItem, final int nbTotalRepairedDays) {
- final BigDecimal amount = InvoiceDateUtils.calculateProrationBetweenDates(startDate, endDate, nbTotalRepairedDays).multiply(repairedItem.getRate()).setScale(NUMBER_OF_DECIMALS, ROUNDING_MODE);
- return new RecurringInvoiceItem(repairedItem.getInvoiceId(),
- repairedItem.getAccountId(),
- repairedItem.getBundleId(),
- repairedItem.getSubscriptionId(),
- repairedItem.getPlanName(),
- repairedItem.getPhaseName(),
- startDate,
- endDate,
- amount,
- repairedItem.getRate(),
- repairedItem.getCurrency());
-
- }
-
private BillingMode instantiateBillingMode(final BillingModeType billingMode) {
switch (billingMode) {
case IN_ADVANCE:
diff --git a/invoice/src/main/java/com/ning/billing/invoice/tree/AccountItemTree.java b/invoice/src/main/java/com/ning/billing/invoice/tree/AccountItemTree.java
index 53cc8cc..b7f3acc 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/tree/AccountItemTree.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/tree/AccountItemTree.java
@@ -16,12 +16,20 @@
package com.ning.billing.invoice.tree;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import javax.annotation.Nullable;
+
import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
public class AccountItemTree {
@@ -33,23 +41,49 @@ public class AccountItemTree {
this.subscriptionItemTree = new HashMap<UUID, SubscriptionItemTree>();
}
- public void addItem(final InvoiceItem item) {
- if (!subscriptionItemTree.containsKey(item.getSubscriptionId())) {
- subscriptionItemTree.put(item.getSubscriptionId(), new SubscriptionItemTree(item.getSubscriptionId()));
+ public void addItem(final InvoiceItem item, final List<InvoiceItem> allItems) {
+ if (item.getInvoiceItemType() != InvoiceItemType.RECURRING && item.getInvoiceItemType() != InvoiceItemType.REPAIR_ADJ) {
+ return;
}
- final SubscriptionItemTree tree = subscriptionItemTree.get(item.getSubscriptionId());
+ final UUID subscriptionId = getSubscriptionId(item, allItems);
+
+ if (!subscriptionItemTree.containsKey(subscriptionId)) {
+ subscriptionItemTree.put(subscriptionId, new SubscriptionItemTree(subscriptionId));
+ }
+ final SubscriptionItemTree tree = subscriptionItemTree.get(subscriptionId);
tree.addItem(item);
}
- public List<InvoiceItem> computeNewItems(final List<InvoiceItem> proposedItems) {
+ public void build() {
+ for (SubscriptionItemTree tree : subscriptionItemTree.values()) {
+ tree.build();
+ }
+ }
- final SubscriptionItemTree curTree = null;
- for (InvoiceItem cur : proposedItems) {
- // STEPH
- if (curTree == null || curTree.getSubscriptionId().equals("foo")) {
+ public List<InvoiceItem> getCurrentExistingItemsView() {
+ final List<InvoiceItem> result = new ArrayList<InvoiceItem>();
+ for (SubscriptionItemTree tree : subscriptionItemTree.values()) {
+ final List<InvoiceItem> simplifiedView = tree.getSimplifiedView();
+ if (simplifiedView.size() > 0) {
+ result.addAll(simplifiedView);
}
}
- return null;
+ return result;
+ }
+
+ private UUID getSubscriptionId(final InvoiceItem item, final List<InvoiceItem> allItems) {
+ if (item.getInvoiceItemType() == InvoiceItemType.RECURRING) {
+ return item.getSubscriptionId();
+ } else {
+ final InvoiceItem linkedItem = Iterables.tryFind(allItems, new Predicate<InvoiceItem>() {
+ @Override
+ public boolean apply(final InvoiceItem input) {
+ return item.getLinkedItemId().equals(input.getId());
+ }
+ }).get();
+ return linkedItem.getSubscriptionId();
+ }
+
}
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/tree/ItemsInterval.java b/invoice/src/main/java/com/ning/billing/invoice/tree/ItemsInterval.java
index 69552d0..ba5e0d2 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/tree/ItemsInterval.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/tree/ItemsInterval.java
@@ -57,25 +57,19 @@ public class ItemsInterval {
}
}
- public InvoiceItem createRecuringItem(LocalDate startDate, LocalDate endDate) {
- Iterator<InvoiceItem> it = items.iterator();
- while (it.hasNext()) {
- final InvoiceItem cur = it.next();
- if (cur.getInvoiceItemType() == InvoiceItemType.RECURRING) {
- 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;
- }
-
public List<InvoiceItem> getItems() {
return items;
}
- public void build(final List<InvoiceItem> output) {
+ 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>();
@@ -97,7 +91,7 @@ public class ItemsInterval {
break;
case ITEM_ADJ:
- // TODO Not implemented
+ // If item has been adjusted, we assume the item should not be re-invoiced so we leave it in the list.
break;
// Ignored
@@ -122,4 +116,25 @@ public class ItemsInterval {
}
});
}
+
+ 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;
+ }
+
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/tree/NodeInterval.java b/invoice/src/main/java/com/ning/billing/invoice/tree/NodeInterval.java
index e99e5a4..b714f76 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/tree/NodeInterval.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/tree/NodeInterval.java
@@ -49,21 +49,9 @@ public class NodeInterval {
public void build(final List<InvoiceItem> output) {
- /*
- // Compute start and end
- if (isRoot()) {
- this.start = leftChild.getStart();
- NodeInterval cur = leftChild;
- while (cur.getRightSibling() != null) {
- cur = cur.getRightSibling();
- }
- this.end = cur.getEnd();
- }
- */
-
// There is no sub-interval, just add our own items.
if (leftChild == null) {
- items.build(output);
+ items.buildFromItems(output);
return;
}
@@ -71,14 +59,14 @@ public class NodeInterval {
NodeInterval curChild = leftChild;
while (curChild != null) {
if (curChild.getStart().compareTo(curDate) > 0) {
- output.add(items.createRecuringItem(curDate, curChild.getStart()));
+ items.buildForNonRepairedItems(curDate, curChild.getStart(), output);
}
curChild.build(output);
curDate = curChild.getEnd();
curChild = curChild.getRightSibling();
}
if (curDate.compareTo(end) < 0) {
- output.add(items.createRecuringItem(curDate, end));
+ items.buildForNonRepairedItems(curDate, end, output);
}
}
@@ -170,8 +158,15 @@ public class NodeInterval {
prevRebalanced.rightSibling = newNode;
}
+
+ NodeInterval prev = null;
for (NodeInterval cur : toBeRebalanced) {
- newNode.addNode(cur);
+ if (prev == null) {
+ newNode.leftChild = cur;
+ } else {
+ prev.rightSibling = cur;
+ }
+ prev = cur;
}
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/tree/SubscriptionItemTree.java b/invoice/src/main/java/com/ning/billing/invoice/tree/SubscriptionItemTree.java
index 137469a..a5686d6 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/tree/SubscriptionItemTree.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/tree/SubscriptionItemTree.java
@@ -21,6 +21,7 @@ import java.util.List;
import java.util.UUID;
import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
public class SubscriptionItemTree {
@@ -35,6 +36,9 @@ public class SubscriptionItemTree {
}
public void addItem(final InvoiceItem item) {
+ if (item.getInvoiceItemType() != InvoiceItemType.RECURRING && item.getInvoiceItemType() != InvoiceItemType.REPAIR_ADJ) {
+ return;
+ }
root.addItem(item);
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
index 0488412..747b7ca 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
@@ -34,6 +34,7 @@ import org.testng.annotations.Test;
import com.ning.billing.ErrorCode;
import com.ning.billing.account.api.Account;
+import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.catalog.DefaultPrice;
import com.ning.billing.catalog.MockInternationalPrice;
import com.ning.billing.catalog.MockPlan;
@@ -45,6 +46,7 @@ import com.ning.billing.catalog.api.PhaseType;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.clock.ClockMock;
+import com.ning.billing.entity.EntityPersistenceException;
import com.ning.billing.invoice.InvoiceTestSuiteWithEmbeddedDB;
import com.ning.billing.invoice.MockBillingEventSet;
import com.ning.billing.invoice.api.Invoice;
@@ -60,13 +62,11 @@ import com.ning.billing.invoice.model.DefaultInvoicePayment;
import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
import com.ning.billing.invoice.model.RecurringInvoiceItem;
import com.ning.billing.invoice.model.RepairAdjInvoiceItem;
-import com.ning.billing.subscription.api.SubscriptionBase;
-import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
-import com.ning.billing.callcontext.InternalCallContext;
-import com.ning.billing.entity.EntityPersistenceException;
import com.ning.billing.junction.BillingEvent;
import com.ning.billing.junction.BillingEventSet;
import com.ning.billing.junction.BillingModeType;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
import com.google.common.collect.ImmutableMap;
@@ -1155,13 +1155,17 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
assertEquals(invoice.getBalance().compareTo(ZERO), 0);
}
- private SubscriptionBase getZombieSubscription() {
+ private SubscriptionBase getZombieSubscription(UUID subscriptionId) {
final SubscriptionBase subscription = Mockito.mock(SubscriptionBase.class);
Mockito.when(subscription.getId()).thenReturn(UUID.randomUUID());
Mockito.when(subscription.getBundleId()).thenReturn(UUID.randomUUID());
return subscription;
}
+ private SubscriptionBase getZombieSubscription() {
+ return getZombieSubscription(UUID.randomUUID());
+ }
+
@Test(groups = "slow")
public void testInvoiceForFreeTrialWithRecurringDiscount() throws InvoiceApiException, CatalogApiException {
final Currency currency = Currency.USD;
@@ -1281,7 +1285,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
((ClockMock) clock).setDay(startDate);
final LocalDate recuringStartDate = clock.getUTCNow().plusDays(30).toLocalDate();
- final LocalDate recuringEndDate = clock.getUTCNow().plusDays(30).toLocalDate();
+ final LocalDate recuringEndDate = recuringStartDate.plusMonths(1);
final LocalDate targetDate = recuringStartDate.plusDays(1);
// FIRST CREATE INITIAL INVOICE WITH ONE RECURRING ITEM
@@ -1316,7 +1320,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// NOW COMPUTE A DIFFERENT ITEM TO TRIGGER REPAIR
final BillingEventSet events = new MockBillingEventSet();
- final SubscriptionBase subscription = getZombieSubscription();
+ final SubscriptionBase subscription = getZombieSubscription(subscriptionId);
final Plan plan = Mockito.mock(Plan.class);
Mockito.when(plan.getName()).thenReturn("plan");
@@ -1324,18 +1328,15 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final PlanPhase phase1 = Mockito.mock(PlanPhase.class);
Mockito.when(phase1.getName()).thenReturn("plan-phase1");
- final PlanPhase phase2 = Mockito.mock(PlanPhase.class);
- Mockito.when(phase2.getName()).thenReturn("plan-phase2");
-
final BillingEvent event1 = invoiceUtil.createMockBillingEvent(null, subscription, recuringStartDate.toDateTimeAtStartOfDay(), plan, phase1, null,
TEN, Currency.USD,
- BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
+ BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
"new-event", 1L, SubscriptionBaseTransitionType.CREATE);
events.add(event1);
final Invoice newInvoice = generator.generateInvoice(UUID.randomUUID(), events, invoices, targetDate, Currency.USD);
invoiceUtil.createInvoice(newInvoice, true, context);
- // VERIFY THAT WE STILL HAVE ONLY 2 ITEMS, MENAING THERE WERE NO REPAIR AND NO CBA GENERATED
+ // VERIFY THAT WE STILL HAVE ONLY 2 ITEMS, MEANING THERE WERE NO REPAIR AND NO CBA GENERATED
final Invoice firstInvoice = new DefaultInvoice(invoiceDao.getById(invoiceId, context));
assertNotNull(firstInvoice);
assertEquals(firstInvoice.getInvoiceItems().size(), 2);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorRepairUnit.java b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorRepairUnit.java
index 569812f..cb02de4 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorRepairUnit.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorRepairUnit.java
@@ -378,97 +378,4 @@ public class TestDefaultInvoiceGeneratorRepairUnit extends InvoiceTestSuiteNoDB
final InvoiceItem invoiceItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, otherSubscriptionId, planName, phaseName, startRepair, endDate, BigDecimal.TEN, BigDecimal.TEN, currency);
assertFalse(defaultInvoiceGenerator.isRepareeIncludedInRepair(repairItem, repairedItem.getSubscriptionId(), invoiceItem));
}
-
- /*********************************** removeProposedRepareesForPartialrepair logic ********************************/
-
- //
- // Test removal of proposed item after a repair, scenario 1:
- //
- // 1. Initially bill the full period:
- // repairedItem
- // |-----------------------------------------------|
- //
- // 2. Block subscription -> repair
- // reparee1 repaired
- // |-------------|---------------------------------|
- //
- // 3. Unblock later -> needs to bill for the last part:
- // * proposed items = {reparee1; reparee2}
- //
- // reparee1 reparee2
- // |-------------|--------------------|------------|
- //
- // => Code should detect that reparee1 was already accounted for, but not reparee2
- // and therefore only reparee2 should remain in the proposed list.
- //
- @Test(groups = "fast")
- public void testRemoveProposedRepareeForPartialRepair1() {
-
- final LocalDate startDate = new LocalDate(2012, 6, 30);
- final LocalDate blockDate = new LocalDate(2012, 7, 10);
- final LocalDate unblockDate = new LocalDate(2012, 7, 23);
- final LocalDate endDate = new LocalDate(2012, 7, 31);
-
- final BigDecimal someAmount = new BigDecimal("100.00");
-
- final List<InvoiceItem> existing = new LinkedList<InvoiceItem>();
- final InvoiceItem repairedItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, someAmount, someAmount, currency);
- existing.add(repairedItem);
-
- final List<InvoiceItem> proposed = new LinkedList<InvoiceItem>();
- final InvoiceItem reparee1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, blockDate, someAmount, someAmount, currency);
- proposed.add(reparee1);
- final InvoiceItem reparee2 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, unblockDate, endDate, someAmount, someAmount, currency);
- proposed.add(reparee2);
-
- final InvoiceItem repairItem = new RepairAdjInvoiceItem(invoiceId, accountId, blockDate, endDate, someAmount, currency, repairedItem.getId());
-
- defaultInvoiceGenerator.removeProposedRepareesForPartialrepair(repairedItem, repairItem, proposed);
-
- assertEquals(proposed.size(), 1);
- assertTrue(proposed.get(0).equals(reparee2));
- }
-
- //
- // Test removal of proposed item after a repair, scenario 2:
- //
- // 1. Initially bill the full period:
- // repairedItem
- // |-----------------------------------------------|
- //
- // 2. Block and Unblock later (SAME TIME). Only notifies invoice at the unblock time
- // * proposed items = {reparee1; reparee2}
- //
- // reparee1 repaired reparee2
- // |-------------|--------------------|------------|
- //
- // => Code should detect that both reparee1 and reparee2 were already accounted for, so
- // nothing should stay in the proposed list.
- //
- @Test(groups = "fast")
- public void testRemoveProposedRepareeForPartialRepair2() {
-
- final LocalDate startDate = new LocalDate(2012, 6, 30);
- final LocalDate blockDate = new LocalDate(2012, 7, 10);
- final LocalDate unblockDate = new LocalDate(2012, 7, 23);
- final LocalDate endDate = new LocalDate(2012, 7, 31);
-
- final BigDecimal someAmount = new BigDecimal("100.00");
-
- final List<InvoiceItem> existing = new LinkedList<InvoiceItem>();
- final InvoiceItem repairedItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, someAmount, someAmount, currency);
- existing.add(repairedItem);
-
- final List<InvoiceItem> proposed = new LinkedList<InvoiceItem>();
- final InvoiceItem reparee1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, blockDate, someAmount, someAmount, currency);
- proposed.add(reparee1);
- final InvoiceItem reparee2 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, unblockDate, endDate, someAmount, someAmount, currency);
- proposed.add(reparee2);
-
- final InvoiceItem repairItem = new RepairAdjInvoiceItem(invoiceId, accountId, blockDate, unblockDate, someAmount, currency, repairedItem.getId());
-
- defaultInvoiceGenerator.removeProposedRepareesForPartialrepair(repairedItem, repairItem, proposed);
-
- assertEquals(proposed.size(), 0);
- }
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorUnit.java b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorUnit.java
index 5d02e64..508be54 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorUnit.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorUnit.java
@@ -55,48 +55,6 @@ public class TestDefaultInvoiceGeneratorUnit extends InvoiceTestSuiteNoDB {
this.defaultInvoiceGenerator = (DefaultInvoiceGenerator) generator;
}
- @Test(groups = "fast")
- public void testRemoveCancellingInvoiceItemsFixedPrice() {
- final LocalDate startDate = clock.getUTCToday();
- final LocalDate endDate = startDate.plusDays(30);
- final LocalDate nextEndDate = startDate.plusMonths(1);
-
- final BigDecimal amount = new BigDecimal("12.00");
- final BigDecimal rate2 = new BigDecimal("14.85");
- final BigDecimal amount2 = rate2;
- final List<InvoiceItem> items = new LinkedList<InvoiceItem>();
- final InvoiceItem item1 = new FixedPriceInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, amount, currency);
- items.add(item1);
- items.add(new RepairAdjInvoiceItem(invoiceId, accountId, startDate, endDate, amount.negate(), currency, item1.getId()));
- items.add(new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, endDate, nextEndDate, amount2, rate2, currency));
- defaultInvoiceGenerator.removeRepairedAndRepairInvoiceItems(items, new LinkedList<InvoiceItem>());
- assertEquals(items.size(), 1);
- final InvoiceItem leftItem = items.get(0);
- assertEquals(leftItem.getInvoiceItemType(), InvoiceItemType.RECURRING);
- assertEquals(leftItem.getAmount(), amount2);
- }
-
- @Test(groups = "fast")
- public void testRemoveCancellingInvoiceItemsRecurringPrice() {
- final LocalDate startDate = clock.getUTCToday();
- final LocalDate endDate = startDate.plusDays(30);
- final LocalDate nextEndDate = startDate.plusMonths(1);
-
- final BigDecimal rate1 = new BigDecimal("12.00");
- final BigDecimal amount1 = rate1;
- final BigDecimal rate2 = new BigDecimal("14.85");
- final BigDecimal amount2 = rate2;
- final List<InvoiceItem> items = new LinkedList<InvoiceItem>();
- final InvoiceItem item1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency);
- items.add(item1);
- items.add(new RepairAdjInvoiceItem(invoiceId, accountId, startDate, endDate, amount1.negate(), currency, item1.getId()));
- items.add(new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, endDate, nextEndDate, amount2, rate2, currency));
- defaultInvoiceGenerator.removeRepairedAndRepairInvoiceItems(items, new LinkedList<InvoiceItem>());
- assertEquals(items.size(), 1);
- final InvoiceItem leftItem = items.get(0);
- assertEquals(leftItem.getInvoiceItemType(), InvoiceItemType.RECURRING);
- assertEquals(leftItem.getAmount(), amount2);
- }
@Test(groups = "fast")
public void testRemoveDuplicatedInvoiceItemsShouldNotThrowIllegalStateExceptionOne() {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tree/TestSubscriptionItemTree.java b/invoice/src/test/java/com/ning/billing/invoice/tree/TestSubscriptionItemTree.java
index 912ae26..eb13737 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tree/TestSubscriptionItemTree.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tree/TestSubscriptionItemTree.java
@@ -25,6 +25,7 @@ import org.testng.annotations.Test;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.model.ItemAdjInvoiceItem;
import com.ning.billing.invoice.model.RecurringInvoiceItem;
import com.ning.billing.invoice.model.RepairAdjInvoiceItem;
@@ -189,44 +190,116 @@ public class TestSubscriptionItemTree /* extends InvoiceTestSuiteNoDB */ {
verifyResult(tree.getSimplifiedView(), expectedResult);
}
- @Test(groups = "fast", enabled = false)
- public void testRepairAndInvoiceItemAdj() {
+
+ @Test(groups = "fast")
+ public void testInvoiceItemAdj() {
final LocalDate startDate = new LocalDate(2014, 1, 1);
+ final LocalDate itemAdjDate = new LocalDate(2014, 1, 7);
final LocalDate endDate = new LocalDate(2014, 2, 1);
- final LocalDate blockStart1 = new LocalDate(2014, 1, 8);
- final LocalDate unblockStart1 = new LocalDate(2014, 1, 10);
-
- final LocalDate blockStart2 = new LocalDate(2014, 1, 17);
- final LocalDate unblockStart2 = new LocalDate(2014, 1, 23);
final BigDecimal rate1 = new BigDecimal("12.00");
final BigDecimal amount1 = rate1;
final InvoiceItem initial = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency);
- final InvoiceItem block1 = new RepairAdjInvoiceItem(invoiceId, accountId, blockStart1, unblockStart1, amount1.negate(), currency, initial.getId());
- final InvoiceItem block2 = new RepairAdjInvoiceItem(invoiceId, accountId, blockStart2, unblockStart2, amount1.negate(), currency, initial.getId());
+ final InvoiceItem adj = new ItemAdjInvoiceItem(initial, itemAdjDate, amount1, currency);
final List<InvoiceItem> expectedResult = Lists.newLinkedList();
- final InvoiceItem expected1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, blockStart1, BigDecimal.ONE, rate1, currency);
+ expectedResult.add(initial);
+
+ // First test with items in order
+ SubscriptionItemTree tree = new SubscriptionItemTree(subscriptionId);
+ tree.addItem(initial);
+ tree.addItem(adj);
+ tree.build();
+ verifyResult(tree.getSimplifiedView(), expectedResult);
+ }
+
+
+ @Test(groups = "fast")
+ public void testBlockAcrossPeriod() {
+
+ final LocalDate startDate1 = new LocalDate(2014, 1, 1);
+ final LocalDate blockDate = new LocalDate(2014, 1, 25);
+ final LocalDate startDate2 = new LocalDate(2014, 2, 1);
+ final LocalDate unblockDate = new LocalDate(2014, 2, 7);
+ final LocalDate endDate = new LocalDate(2014, 3, 1);
+
+
+ final BigDecimal rate1 = new BigDecimal("12.00");
+ final BigDecimal amount1 = rate1;
+
+ final InvoiceItem first = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate1, startDate2, amount1, rate1, currency);
+ final InvoiceItem second = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate2, endDate, amount1, rate1, currency);
+ final InvoiceItem block1 = new RepairAdjInvoiceItem(invoiceId, accountId, blockDate, startDate2, amount1.negate(), currency, first.getId());
+ final InvoiceItem block2 = new RepairAdjInvoiceItem(invoiceId, accountId, startDate2, unblockDate, amount1.negate(), currency, first.getId());
+
+ final List<InvoiceItem> expectedResult = Lists.newLinkedList();
+ final InvoiceItem expected1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate1, blockDate, new BigDecimal("9.29"), rate1, currency);
+ final InvoiceItem expected2 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, unblockDate, endDate, new BigDecimal("9.43"), rate1, currency);
expectedResult.add(expected1);
- expectedResult.add(block1);
- final InvoiceItem expected2 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, unblockStart1, blockStart2, BigDecimal.ONE, rate1, currency);
expectedResult.add(expected2);
- expectedResult.add(block2);
- final InvoiceItem expected3 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, unblockStart2, endDate, BigDecimal.ONE, rate1, currency);
- expectedResult.add(expected3);
// First test with items in order
SubscriptionItemTree tree = new SubscriptionItemTree(subscriptionId);
- tree.addItem(initial);
+ tree.addItem(first);
+ tree.addItem(second);
tree.addItem(block1);
tree.addItem(block2);
tree.build();
verifyResult(tree.getSimplifiedView(), expectedResult);
}
+ @Test(groups = "fast")
+ public void testAnnualFullRepairFollowedByMonthly() {
+
+ final LocalDate startDate = new LocalDate(2014, 1, 1);
+ final LocalDate firstMonthlyEndDate = new LocalDate(2014, 2, 1);
+ final LocalDate secondMonthlyEndDate = new LocalDate(2014, 3, 1);
+ final LocalDate endDate = new LocalDate(2015, 2, 1);
+
+ final BigDecimal rate1 = new BigDecimal("120.00");
+ final BigDecimal amount1 = rate1;
+
+ final BigDecimal rate2 = new BigDecimal("10.00");
+ final BigDecimal amount2 = rate2;
+
+ final InvoiceItem annual = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency);
+ final InvoiceItem repair = new RepairAdjInvoiceItem(invoiceId, accountId, startDate, endDate, amount1.negate(), currency, annual.getId());
+ final InvoiceItem monthly1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, "someelse", "someelse", startDate, firstMonthlyEndDate, amount2, rate2, currency);
+ final InvoiceItem monthly2 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, "someelse", "someelse", firstMonthlyEndDate, secondMonthlyEndDate, amount2, rate2, currency);
+
+ final List<InvoiceItem> expectedResult = Lists.newLinkedList();
+ expectedResult.add(monthly1);
+ expectedResult.add(monthly2);
+
+ SubscriptionItemTree tree = new SubscriptionItemTree(subscriptionId);
+ tree.addItem(annual);
+ tree.addItem(repair);
+ tree.addItem(monthly1);
+ tree.addItem(monthly2);
+ tree.build();
+ verifyResult(tree.getSimplifiedView(), expectedResult);
+
+
+ tree = new SubscriptionItemTree(subscriptionId);
+ tree.addItem(monthly1);
+ tree.addItem(repair);
+ tree.addItem(annual);
+ tree.addItem(monthly2);
+ tree.build();
+ verifyResult(tree.getSimplifiedView(), expectedResult);
+
+ tree = new SubscriptionItemTree(subscriptionId);
+ tree.addItem(monthly1);
+ tree.addItem(monthly2);
+ tree.addItem(annual);
+ tree.addItem(repair);
+ tree.build();
+ verifyResult(tree.getSimplifiedView(), expectedResult);
+ }
+
private void verifyResult(final List<InvoiceItem> result, final List<InvoiceItem> expectedResult) {
assertEquals(result.size(), expectedResult.size());
for (int i = 0; i < expectedResult.size(); i++) {