killbill-memoizeit
Changes
invoice/src/test/java/com/ning/billing/invoice/generator/DefaultInvoiceGeneratorWithSwitchRepairLogic.java 7(+0 -7)
invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java 38(+19 -19)
Details
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 d3342c0..4d82d6d 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
@@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory;
import com.ning.billing.ErrorCode;
import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.clock.Clock;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.InvoiceItem;
@@ -49,47 +50,43 @@ 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.clock.Clock;
-import com.ning.billing.util.config.InvoiceConfig;
import com.ning.billing.junction.BillingEvent;
import com.ning.billing.junction.BillingEventSet;
import com.ning.billing.junction.BillingModeType;
+import com.ning.billing.util.config.InvoiceConfig;
-import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.inject.Inject;
-
/**
* Terminology for repair scenarii:
- *
+ * <p/>
* - A 'repaired' item is an item that was generated and that needs to be repaired because the plan changed for that subscription on that period of time
* - The 'repair' item is the item that cancels the (to be) repaired item; the repair item amount might not match (to be) repaired item because:
- * * the (to be) repaired item was already adjusted so we will only repair what is left
- * * in case of partial repair we only repair the part that is not used
+ * * the (to be) repaired item was already adjusted so we will only repair what is left
+ * * in case of partial repair we only repair the part that is not used
* - The 'reparee' item is only present on disk-- in the existing item list -- in case of full repair; in that case it represents the portion of the item that should still
- * be invoiced for the plan of the repaired item. In case of partial repair it is merged with the repair item and does not exist except as a virtual item in the proposed list
- *
- *
- *
+ * be invoiced for the plan of the repaired item. In case of partial repair it is merged with the repair item and does not exist except as a virtual item in the proposed list
+ * <p/>
+ * <p/>
+ * <p/>
* Example. We had a 20 subscription for a given period; we charged that amount and later discovered that only 3/4 of the time period were used after which the subscription was cancelled (immediate canellation)
- *
+ * <p/>
* Full repair logic:
- *
+ * <p/>
* Invoice 1: Invoice 2:
- * +20 (repaired) +5 (reparee)
- * -20 (repair)
- *
+ * +20 (repaired) +5 (reparee)
+ * -20 (repair)
+ * <p/>
* Partial repair logic:
- *
+ * <p/>
* Invoice 1: Invoice 2: (N/A)
- * +20 (repaired)
- * -15 (repair)
- *
+ * +20 (repaired)
+ * -15 (repair)
+ * <p/>
* The current version of the code uses partial repair logic but is able to deal with 'full repair' scenarii.
- *
*/
public class DefaultInvoiceGenerator implements InvoiceGenerator {
@@ -193,15 +190,12 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
/**
* Add the repair item for the (yet to be) repairedItem. It will merge the candidateRepairItem with reparee item
*
- *
- *
* @param repairedItem the item being repaired
* @param candidateRepairItem the repair item we would have if we were to repair the full period
* @param proposedItems the list of proposed items
*/
void addRepairItem(final InvoiceItem repairedItem, final RepairAdjInvoiceItem candidateRepairItem, final List<InvoiceItem> proposedItems) {
-
int nbTotalRepaireeDays = 0;
// totalRepareeItemAmount is negative and represents the portion left after we removed the adjustments for the total period for all the reparees combined
@@ -237,20 +231,21 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
final Iterator<InvoiceItem> it = reparees.iterator();
while (it.hasNext()) {
final InvoiceItem nextReparee = it.next();
- if (prevReparee != null) {
+ if (prevReparee != null || nextReparee.getStartDate().compareTo(repairedItem.getStartDate()) > 0) {
+ final LocalDate repairedStartDate = (prevReparee == null) ? repairedItem.getStartDate() : prevReparee.getEndDate();
+ final LocalDate repairedEndDateDate = nextReparee.getStartDate();
// repairItemAmount is an approximation of the exact amount by simply prorating totalRepareeItemAmount in the repair period; we make sure last item is calculated based
// on what is left so the sum of all repairs amount is exactly correct
final BigDecimal repairItemAmount = (nextReparee.getEndDate().compareTo(candidateRepairItem.getEndDate()) != 0) ?
- InvoiceDateUtils.calculateProrationBetweenDates(prevReparee.getEndDate(), nextReparee.getStartDate(), nbTotalRepairedDays).multiply(totalRepareeItemAmount) :
+ InvoiceDateUtils.calculateProrationBetweenDates(repairedStartDate, repairedEndDateDate, nbTotalRepairedDays).multiply(totalRepareeItemAmount) :
totalRepareeItemAmount.subtract(totalRepairItemAmount);
totalRepairItemAmount = totalRepairItemAmount.add(repairItemAmount);
- final RepairAdjInvoiceItem repairItem = new RepairAdjInvoiceItem(candidateRepairItem.getInvoiceId(), candidateRepairItem.getAccountId(), prevReparee.getEndDate(), nextReparee.getStartDate(), repairItemAmount, candidateRepairItem.getCurrency(), repairedItem.getId());
+ final RepairAdjInvoiceItem repairItem = new RepairAdjInvoiceItem(candidateRepairItem.getInvoiceId(), candidateRepairItem.getAccountId(), repairedStartDate, repairedEndDateDate, repairItemAmount, candidateRepairItem.getCurrency(), repairedItem.getId());
repairedItems.add(repairItem);
}
prevReparee = nextReparee;
}
-
// In case we end up with a repair up to the service endDate we need to add this extra item-- this is the 'classic' case with one repairee/repair item
if (prevReparee.getEndDate().compareTo(candidateRepairItem.getEndDate()) != 0) {
final BigDecimal repairItemAmount = totalRepareeItemAmount.subtract(totalRepairItemAmount);
@@ -272,7 +267,6 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
* @param invoiceItem any invoice item to compare to
* @return true if invoiceItem is the reparee for that repaired invoice item
*/
- @VisibleForTesting
boolean isRepareeItemForRepairedItem(final InvoiceItem repairedInvoiceItem, final InvoiceItem invoiceItem) {
return !repairedInvoiceItem.getId().equals(invoiceItem.getId()) &&
repairedInvoiceItem.getInvoiceItemType().equals(invoiceItem.getInvoiceItemType()) &&
@@ -295,6 +289,20 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
Objects.firstNonNull(repairedInvoiceItem.getRate(), BigDecimal.ZERO).compareTo(Objects.firstNonNull(invoiceItem.getRate(), BigDecimal.ZERO)) == 0;
}
+ /**
+ * Check whether the repair invoice item overlaps the proposed item
+ *
+ * @param repairInvoiceItem the repair invoice item associated to the repaired item for which we pass the subscriptionId
+ * @param repairedSubscriptionId the subscriptionId for which this repair points to
+ * @param invoiceItem an invoice item to compare to
+ * @return
+ */
+ boolean isRepareeIncludedInRepair(final InvoiceItem repairInvoiceItem, final UUID repairedSubscriptionId, final InvoiceItem invoiceItem) {
+ return invoiceItem.getSubscriptionId().equals(repairedSubscriptionId) &&
+ repairInvoiceItem.getStartDate().compareTo(invoiceItem.getStartDate()) <= 0 &&
+ (invoiceItem.getEndDate() != null &&
+ repairInvoiceItem.getEndDate().compareTo(invoiceItem.getEndDate()) >= 0);
+ }
// We check to see if there are any adjustments that point to the item we are trying to repair
// If we did any CREDIT_ADJ or REFUND_ADJ, then we unfortunately we can't know what is the intent
@@ -376,14 +384,16 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
final List<UUID> itemsToRemove = new ArrayList<UUID>();
for (final InvoiceItem item : existingItems) {
if (item.getInvoiceItemType() == InvoiceItemType.REPAIR_ADJ) {
- itemsToRemove.add(item.getId());
- itemsToRemove.add(item.getLinkedItemId());
- final InvoiceItem repairedInvoiceItem = getRepairedInvoiceItem(item.getLinkedItemId(), existingItems);
- // if this is a full repair there is no reparee so nothing to remove; if not reparee needs to be removed from proposed list
- if (!isFullRepair(repairedInvoiceItem, item, existingItems)) {
- removeProposedRepareeForPartialrepair(repairedInvoiceItem, proposedItems);
+ // Assign for terminology purpose
+ final InvoiceItem repairItem = item;
+ itemsToRemove.add(repairItem.getId());
+ itemsToRemove.add(repairItem.getLinkedItemId());
+ final InvoiceItem repairedItem = getRepairedInvoiceItem(repairItem.getLinkedItemId(), existingItems);
+ // if this is a full repair there is no reparee so nothing to remove; if not reparees need to be removed from proposed list
+ if (!isFullRepair(repairedItem, repairItem, existingItems)) {
+ removeProposedRepareesForPartialrepair(repairedItem, repairItem, proposedItems);
}
}
}
@@ -418,18 +428,20 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
* @param repairedItem the repaired item
* @param proposedItems the list of existing items
*/
- protected void removeProposedRepareeForPartialrepair(final InvoiceItem repairedItem, final List<InvoiceItem> proposedItems) {
+ protected void removeProposedRepareesForPartialrepair(final InvoiceItem repairedItem, final InvoiceItem repairItem, final List<InvoiceItem> proposedItems) {
final Iterator<InvoiceItem> it = proposedItems.iterator();
while (it.hasNext()) {
final InvoiceItem cur = it.next();
- if (isRepareeItemForRepairedItem(repairedItem, cur)) {
+ 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)) {
it.remove();
- break;
}
}
}
-
private InvoiceItem getRepairedInvoiceItem(final UUID repairedInvoiceItemId, final List<InvoiceItem> existingItems) {
for (InvoiceItem cur : existingItems) {
if (cur.getId().equals(repairedInvoiceItemId)) {
@@ -439,7 +451,6 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
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>();
diff --git a/invoice/src/test/java/com/ning/billing/invoice/generator/DefaultInvoiceGeneratorWithSwitchRepairLogic.java b/invoice/src/test/java/com/ning/billing/invoice/generator/DefaultInvoiceGeneratorWithSwitchRepairLogic.java
index f00172a..34c48c7 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/generator/DefaultInvoiceGeneratorWithSwitchRepairLogic.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/generator/DefaultInvoiceGeneratorWithSwitchRepairLogic.java
@@ -48,13 +48,6 @@ public class DefaultInvoiceGeneratorWithSwitchRepairLogic extends DefaultInvoice
}
@Override
- protected void removeProposedRepareeForPartialrepair(final InvoiceItem repairedItem, final List<InvoiceItem> proposedItems) {
- if (repairtLogic == REPAIR_INVOICE_LOGIC.PARTIAL_REPAIR) {
- super.removeProposedRepareeForPartialrepair(repairedItem, proposedItems);
- }
- }
-
- @Override
void addRepairItem(final InvoiceItem repairedItem, final RepairAdjInvoiceItem candidateRepairItem, final List<InvoiceItem> proposedItems) {
if (repairtLogic == REPAIR_INVOICE_LOGIC.PARTIAL_REPAIR) {
super.addRepairItem(repairedItem, candidateRepairItem, proposedItems);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java
index 91cc531..fce3a9d 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java
@@ -130,7 +130,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
public void testWithSingleMonthlyEvent() throws InvoiceApiException, CatalogApiException {
final BillingEventSet events = new MockBillingEventSet();
- final SubscriptionBase sub = createZombieSubscription();
+ final SubscriptionBase sub = createSubscription();
final LocalDate startDate = invoiceUtil.buildDate(2011, 9, 1);
final Plan plan = new MockPlan();
@@ -150,11 +150,11 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
assertEquals(invoice.getInvoiceItems().get(0).getSubscriptionId(), sub.getId());
}
- private SubscriptionBase createZombieSubscription() {
- return createZombieSubscription(UUID.randomUUID(), UUID.randomUUID());
+ private SubscriptionBase createSubscription() {
+ return createSubscription(UUID.randomUUID(), UUID.randomUUID());
}
- private SubscriptionBase createZombieSubscription(final UUID subscriptionId, final UUID bundleId) {
+ private SubscriptionBase createSubscription(final UUID subscriptionId, final UUID bundleId) {
final SubscriptionBase sub = Mockito.mock(SubscriptionBase.class);
Mockito.when(sub.getId()).thenReturn(subscriptionId);
Mockito.when(sub.getBundleId()).thenReturn(bundleId);
@@ -165,7 +165,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
@Test(groups = "fast")
public void testSimpleWithTimeZone() throws InvoiceApiException, CatalogApiException {
final UUID accountId = UUID.randomUUID();
- final SubscriptionBase sub = createZombieSubscription();
+ final SubscriptionBase sub = createSubscription();
final Plan plan = new MockPlan();
final BigDecimal rate = TEN;
final PlanPhase phase = createMockMonthlyPlanPhase(rate);
@@ -193,7 +193,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
@Test(groups = "fast")
public void testSimpleWithSingleDiscountEvent() throws Exception {
final UUID accountId = UUID.randomUUID();
- final SubscriptionBase sub = createZombieSubscription();
+ final SubscriptionBase sub = createSubscription();
final Plan plan = new MockPlan("Plan with a single discount phase");
final PlanPhase phaseEvergreen = createMockMonthlyPlanPhase(EIGHT, PhaseType.DISCOUNT);
final int bcdLocal = 16;
@@ -216,7 +216,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
public void testWithSingleMonthlyEventWithLeadingProRation() throws InvoiceApiException, CatalogApiException {
final BillingEventSet events = new MockBillingEventSet();
- final SubscriptionBase sub = createZombieSubscription();
+ final SubscriptionBase sub = createSubscription();
final LocalDate startDate = invoiceUtil.buildDate(2011, 9, 1);
final Plan plan = new MockPlan();
@@ -250,7 +250,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final BigDecimal rate2 = TEN;
final PlanPhase phase2 = createMockMonthlyPlanPhase(rate2);
- final SubscriptionBase sub = createZombieSubscription();
+ final SubscriptionBase sub = createSubscription();
final BillingEvent event1 = createBillingEvent(sub.getId(), sub.getBundleId(), invoiceUtil.buildDate(2011, 9, 1), plan1, phase1, 1);
events.add(event1);
@@ -275,7 +275,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final BigDecimal rate1 = FIVE;
final PlanPhase phase1 = createMockMonthlyPlanPhase(rate1);
- final SubscriptionBase sub = createZombieSubscription();
+ final SubscriptionBase sub = createSubscription();
final BillingEvent event1 = createBillingEvent(sub.getId(), sub.getBundleId(), invoiceUtil.buildDate(2011, 9, 1), plan1, phase1, 1);
events.add(event1);
@@ -312,7 +312,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final BigDecimal rate1 = FIVE;
final PlanPhase phase1 = createMockMonthlyPlanPhase(rate1);
- final SubscriptionBase sub = createZombieSubscription();
+ final SubscriptionBase sub = createSubscription();
final BillingEvent event1 = createBillingEvent(sub.getId(), sub.getBundleId(), invoiceUtil.buildDate(2011, 9, 1), plan1, phase1, 1);
events.add(event1);
@@ -339,7 +339,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
public void testSingleEventWithExistingInvoice() throws InvoiceApiException, CatalogApiException {
final BillingEventSet events = new MockBillingEventSet();
- final SubscriptionBase sub = createZombieSubscription();
+ final SubscriptionBase sub = createSubscription();
final LocalDate startDate = invoiceUtil.buildDate(2011, 9, 1);
final Plan plan1 = new MockPlan();
@@ -554,7 +554,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
@Test(groups = "fast")
public void testFixedPriceLifeCycle() throws InvoiceApiException {
final UUID accountId = UUID.randomUUID();
- final SubscriptionBase subscription = createZombieSubscription();
+ final SubscriptionBase subscription = createSubscription();
final Plan plan = new MockPlan("plan 1");
final MockInternationalPrice zeroPrice = new MockInternationalPrice(new DefaultPrice(ZERO, Currency.USD));
@@ -765,7 +765,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
private BillingEvent createBillingEvent(final UUID subscriptionId, final UUID bundleId, final LocalDate startDate,
final Plan plan, final PlanPhase planPhase, final int billCycleDayLocal) throws CatalogApiException {
- final SubscriptionBase sub = createZombieSubscription(subscriptionId, bundleId);
+ final SubscriptionBase sub = createSubscription(subscriptionId, bundleId);
final Currency currency = Currency.USD;
return invoiceUtil.createMockBillingEvent(null, sub, startDate.toDateTimeAtStartOfDay(), plan, planPhase,
@@ -794,7 +794,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
// create a base plan on April 25th
final UUID accountId = UUID.randomUUID();
- final SubscriptionBase baseSubscription = createZombieSubscription();
+ final SubscriptionBase baseSubscription = createSubscription();
final Plan basePlan = new MockPlan("base Plan");
final MockInternationalPrice price5 = new MockInternationalPrice(new DefaultPrice(FIVE, Currency.USD));
@@ -816,12 +816,12 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
// create 2 add ons on April 28th
final LocalDate april28 = new LocalDate(2012, 4, 28);
- final SubscriptionBase addOnSubscription1 = createZombieSubscription();
+ final SubscriptionBase addOnSubscription1 = createSubscription();
final Plan addOn1Plan = new MockPlan("add on 1");
final PlanPhase addOn1PlanPhaseEvergreen = new MockPlanPhase(price5, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
events.add(createBillingEvent(addOnSubscription1.getId(), baseSubscription.getBundleId(), april28, addOn1Plan, addOn1PlanPhaseEvergreen, 25));
- final SubscriptionBase addOnSubscription2 = createZombieSubscription();
+ final SubscriptionBase addOnSubscription2 = createSubscription();
final Plan addOn2Plan = new MockPlan("add on 2");
final PlanPhase addOn2PlanPhaseEvergreen = new MockPlanPhase(price20, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
events.add(createBillingEvent(addOnSubscription2.getId(), baseSubscription.getBundleId(), april28, addOn2Plan, addOn2PlanPhaseEvergreen, 25));
@@ -859,7 +859,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
// create a base plan on April 25th
final UUID accountId = UUID.randomUUID();
- final SubscriptionBase originalSubscription = createZombieSubscription();
+ final SubscriptionBase originalSubscription = createSubscription();
final Plan originalPlan = new MockPlan("original plan");
final MockInternationalPrice price10 = new MockInternationalPrice(new DefaultPrice(TEN, Currency.USD));
@@ -883,7 +883,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
// change the plan (i.e. repair) on start date
events.clear();
- final SubscriptionBase newSubscription = createZombieSubscription();
+ final SubscriptionBase newSubscription = createSubscription();
final Plan newPlan = new MockPlan("new plan");
final MockInternationalPrice price5 = new MockInternationalPrice(new DefaultPrice(FIVE, Currency.USD));
final PlanPhase newPlanEvergreen = new MockPlanPhase(price5, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
@@ -937,7 +937,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final MockBillingEventSet events = new MockBillingEventSet();
events.setAccountInvoiceOff(true);
- final SubscriptionBase sub = createZombieSubscription();
+ final SubscriptionBase sub = createSubscription();
final LocalDate startDate = invoiceUtil.buildDate(2011, 9, 1);
final Plan plan = new MockPlan();
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
new file mode 100644
index 0000000..c363267
--- /dev/null
+++ b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGeneratorRepairUnit.java
@@ -0,0 +1,474 @@
+/*
+ * Copyright 2010-2013 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.generator;
+
+import java.math.BigDecimal;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.LocalDate;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.InvoiceTestSuiteNoDB;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
+import com.ning.billing.invoice.model.RecurringInvoiceItem;
+import com.ning.billing.invoice.model.RepairAdjInvoiceItem;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+public class TestDefaultInvoiceGeneratorRepairUnit extends InvoiceTestSuiteNoDB {
+
+ private final UUID invoiceId = UUID.randomUUID();
+ private final UUID accountId = UUID.randomUUID();
+ private final UUID subscriptionId = UUID.randomUUID();
+ private final UUID bundleId = UUID.randomUUID();
+ private final String planName = "my-plan";
+ private final String phaseName = "my-phase";
+ private final Currency currency = Currency.USD;
+
+ private DefaultInvoiceGenerator defaultInvoiceGenerator;
+
+ @BeforeClass(groups = "fast")
+ protected void beforeClass() throws Exception {
+ super.beforeClass();
+ this.defaultInvoiceGenerator = (DefaultInvoiceGenerator) generator;
+ }
+
+ /********************************************* addRepairItem logic ********************************/
+
+ // repairedItem
+ // |-----------------------------------------------|
+ //
+ // proposed1 (result ->) repair proposed2
+ // |-------------|--------------------|------------|
+
+ @Test(groups = "fast")
+ public void testAddRepairedItem1() {
+
+ final LocalDate startDate = new LocalDate(2013, 12, 1);
+ final LocalDate endDate = new LocalDate(2014, 12, 1);
+ final LocalDate endDateProposed1 = new LocalDate(2014, 1, 1);
+ final LocalDate startProposed2 = new LocalDate(2014, 11, 1);
+
+ final BigDecimal rate = new BigDecimal("120.00");
+ final BigDecimal amount = rate;
+
+ final InvoiceItem repairedItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, rate, currency);
+
+ final RepairAdjInvoiceItem candidateRepairItem = new RepairAdjInvoiceItem(repairedItem.getInvoiceId(), repairedItem.getAccountId(), repairedItem.getStartDate(), repairedItem.getEndDate(), repairedItem.getAmount().negate(), repairedItem.getCurrency(), repairedItem.getId());
+ final List<InvoiceItem> proposed = new LinkedList<InvoiceItem>();
+ final InvoiceItem proposed1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDateProposed1, BigDecimal.TEN, rate, currency);
+ proposed.add(proposed1);
+ final InvoiceItem proposed2 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startProposed2, endDate, BigDecimal.TEN, rate, currency);
+ proposed.add(proposed2);
+
+ defaultInvoiceGenerator.addRepairItem(repairedItem, candidateRepairItem, proposed);
+
+ assertEquals(proposed.size(), 1);
+ assertEquals(proposed.get(0).getStartDate(), endDateProposed1);
+ assertEquals(proposed.get(0).getEndDate(), startProposed2);
+ assertEquals(proposed.get(0).getLinkedItemId(), repairedItem.getId());
+ assertEquals(proposed.get(0).getAmount(), new BigDecimal("-100.00"));
+ assertEquals(proposed.get(0).getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
+ }
+
+ // repairedItem
+ // |-----------------------------------------------|
+ //
+ // proposed1 (result ->) repair
+ // |-------------|---------------------------------|
+
+ @Test(groups = "fast")
+ public void testAddRepairedItem2() {
+
+ final LocalDate startDate = new LocalDate(2013, 12, 1);
+ final LocalDate endDate = new LocalDate(2014, 12, 1);
+ final LocalDate endDateProposed1 = new LocalDate(2014, 1, 1);
+
+ final BigDecimal rate = new BigDecimal("120.00");
+ final BigDecimal amount = rate;
+
+ final InvoiceItem repairedItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, rate, currency);
+
+ final RepairAdjInvoiceItem candidateRepairItem = new RepairAdjInvoiceItem(repairedItem.getInvoiceId(), repairedItem.getAccountId(), repairedItem.getStartDate(), repairedItem.getEndDate(), repairedItem.getAmount().negate(), repairedItem.getCurrency(), repairedItem.getId());
+ final List<InvoiceItem> proposed = new LinkedList<InvoiceItem>();
+ final InvoiceItem proposed1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDateProposed1, BigDecimal.TEN, rate, currency);
+ proposed.add(proposed1);
+
+ defaultInvoiceGenerator.addRepairItem(repairedItem, candidateRepairItem, proposed);
+
+ assertEquals(proposed.size(), 1);
+ assertEquals(proposed.get(0).getStartDate(), endDateProposed1);
+ assertEquals(proposed.get(0).getEndDate(), endDate);
+ assertEquals(proposed.get(0).getLinkedItemId(), repairedItem.getId());
+ assertEquals(proposed.get(0).getAmount(), new BigDecimal("-110.00"));
+ assertEquals(proposed.get(0).getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
+ }
+
+
+ // repairedItem
+ // |-----------------------------------------------|
+ //
+ // (result ->) repair proposed1
+ // |----------------------------------|-----------|
+
+ @Test(groups = "fast")
+ public void testAddRepairedItem3() {
+
+ final LocalDate startDate = new LocalDate(2013, 12, 1);
+ final LocalDate endDate = new LocalDate(2014, 12, 1);
+ final LocalDate startDateProposed1 = new LocalDate(2014, 1, 1);
+
+ final BigDecimal rate = new BigDecimal("120.00");
+ final BigDecimal amount = rate;
+
+ final InvoiceItem repairedItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, rate, currency);
+
+ final RepairAdjInvoiceItem candidateRepairItem = new RepairAdjInvoiceItem(repairedItem.getInvoiceId(), repairedItem.getAccountId(), repairedItem.getStartDate(), repairedItem.getEndDate(), repairedItem.getAmount().negate(), repairedItem.getCurrency(), repairedItem.getId());
+ final List<InvoiceItem> proposed = new LinkedList<InvoiceItem>();
+ final InvoiceItem proposed1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDateProposed1, endDate, BigDecimal.TEN, rate, currency);
+ proposed.add(proposed1);
+
+ defaultInvoiceGenerator.addRepairItem(repairedItem, candidateRepairItem, proposed);
+
+ assertEquals(proposed.size(), 1);
+ assertEquals(proposed.get(0).getStartDate(), startDate);
+ assertEquals(proposed.get(0).getEndDate(), startDateProposed1);
+ assertEquals(proposed.get(0).getLinkedItemId(), repairedItem.getId());
+ assertEquals(proposed.get(0).getAmount(), new BigDecimal("-110.00"));
+ assertEquals(proposed.get(0).getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
+ }
+
+ // repairedItem
+ // |---------------------------------------------------|
+ //
+ // proposed1 repair1 proposed2 repair2 proposed3
+ // |----------|-------- |-----------|--------|----------|
+
+ @Test(groups = "fast")
+ public void testAddRepairedItem4() {
+
+ final LocalDate startDate = new LocalDate(2013, 12, 1);
+ final LocalDate endDate = new LocalDate(2014, 12, 1);
+ final LocalDate endDateProposed1 = new LocalDate(2014, 1, 1);
+ final LocalDate startDateProposed2 = new LocalDate(2014, 8, 1);
+ final LocalDate endDateProposed2 = new LocalDate(2014, 9, 1);
+ final LocalDate startDateProposed3 = new LocalDate(2014, 11, 1);
+
+ final BigDecimal rate = new BigDecimal("120.00");
+ final BigDecimal amount = rate;
+
+ final InvoiceItem repairedItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, rate, currency);
+
+ final RepairAdjInvoiceItem candidateRepairItem = new RepairAdjInvoiceItem(repairedItem.getInvoiceId(), repairedItem.getAccountId(), repairedItem.getStartDate(), repairedItem.getEndDate(), repairedItem.getAmount().negate(), repairedItem.getCurrency(), repairedItem.getId());
+ final List<InvoiceItem> proposed = new LinkedList<InvoiceItem>();
+ final InvoiceItem proposed1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDateProposed1, BigDecimal.TEN, rate, currency);
+ proposed.add(proposed1);
+ final InvoiceItem proposed2 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDateProposed2, endDateProposed2, BigDecimal.TEN, rate, currency);
+ proposed.add(proposed2);
+ final InvoiceItem proposed3 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDateProposed3, endDate, BigDecimal.TEN, rate, currency);
+ proposed.add(proposed3);
+
+ defaultInvoiceGenerator.addRepairItem(repairedItem, candidateRepairItem, proposed);
+
+ assertEquals(proposed.size(), 2);
+ assertEquals(proposed.get(0).getStartDate(), endDateProposed1);
+ assertEquals(proposed.get(0).getEndDate(), startDateProposed2);
+ assertEquals(proposed.get(0).getLinkedItemId(), repairedItem.getId());
+ assertEquals(proposed.get(0).getAmount(), new BigDecimal("-69.894000"));
+ assertEquals(proposed.get(0).getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
+
+ assertEquals(proposed.get(1).getStartDate(), endDateProposed2);
+ assertEquals(proposed.get(1).getEndDate(), startDateProposed3);
+ assertEquals(proposed.get(1).getLinkedItemId(), repairedItem.getId());
+ assertEquals(proposed.get(1).getAmount(), new BigDecimal("-20.106000"));
+ assertEquals(proposed.get(1).getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
+ }
+
+
+ /********************************************* addRepairItems logic ********************************/
+
+ @Test(groups = "fast")
+ public void testAddRepairedItemsItemsRecurringPrice() {
+ final LocalDate startDate = new LocalDate(2013, 12, 13);
+ final LocalDate endDate = new LocalDate(2014, 1, 12);
+ final LocalDate nextEndDate = new LocalDate(2014, 1, 13);
+
+ final BigDecimal rate1 = new BigDecimal("12.00");
+ final BigDecimal amount1 = rate1;
+
+ final BigDecimal rate2 = new BigDecimal("14.85");
+ final BigDecimal amount2 = rate2;
+
+ final UUID firstInvoiceId = UUID.randomUUID();
+ final List<InvoiceItem> existing = new LinkedList<InvoiceItem>();
+ final InvoiceItem item1 = new RecurringInvoiceItem(firstInvoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency);
+ existing.add(item1);
+
+ final List<InvoiceItem> proposed = new LinkedList<InvoiceItem>();
+ final InvoiceItem other = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, endDate, nextEndDate, amount2, rate2, currency);
+ proposed.add(other);
+
+ defaultInvoiceGenerator.addRepairItems(existing, proposed);
+ assertEquals(existing.size(), 1);
+ assertEquals(proposed.size(), 2);
+ assertEquals(proposed.get(0), other);
+
+ final InvoiceItem newItem2 = proposed.get(1);
+ assertEquals(newItem2.getInvoiceId(), firstInvoiceId);
+ assertEquals(newItem2.getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
+ assertEquals(newItem2.getAmount(), item1.getAmount().negate());
+ assertEquals(newItem2.getLinkedItemId(), item1.getId());
+ assertEquals(newItem2.getStartDate(), startDate);
+ assertEquals(newItem2.getEndDate(), endDate);
+ }
+
+ /********************************************* isRepareeItemForRepairedItem logic ********************************/
+
+ @Test(groups = "fast")
+ public void testShouldFindRepareeForPartialRepairs() throws Exception {
+ final LocalDate startDate = new LocalDate(2012, 5, 1);
+ final LocalDate endDate = new LocalDate(2012, 6, 1);
+ // Repaired item
+ final InvoiceItem silver = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, BigDecimal.TEN, BigDecimal.TEN, currency);
+
+ // Reparee item
+ final LocalDate actualEndDateSilver = new LocalDate(2012, 5, 10);
+ final InvoiceItem actualSilver = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, actualEndDateSilver, new BigDecimal("3"), BigDecimal.TEN, currency);
+
+ // New item
+ final InvoiceItem gold = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, "new-" + planName, phaseName, actualEndDateSilver, endDate, BigDecimal.TEN, new BigDecimal("15"), currency);
+
+ assertFalse(defaultInvoiceGenerator.isRepareeItemForRepairedItem(silver, silver));
+ assertFalse(defaultInvoiceGenerator.isRepareeItemForRepairedItem(silver, gold));
+ assertTrue(defaultInvoiceGenerator.isRepareeItemForRepairedItem(silver, actualSilver));
+ }
+
+ @Test(groups = "fast")
+ public void testShouldntFindRepareeForFullRepairs() throws Exception {
+ final LocalDate startDate = new LocalDate(2012, 5, 1);
+ final LocalDate endDate = new LocalDate(2013, 5, 1);
+ // Repaired item
+ final InvoiceItem annual = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, BigDecimal.TEN, BigDecimal.TEN, currency);
+
+ // There is no reparee - full repair
+
+ // New item
+ final LocalDate endDate2 = new LocalDate(2012, 6, 1);
+ final InvoiceItem monthly = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, "new-" + planName, phaseName, startDate, endDate2, BigDecimal.TEN, BigDecimal.TEN, currency);
+
+ assertFalse(defaultInvoiceGenerator.isRepareeItemForRepairedItem(annual, annual));
+ assertFalse(defaultInvoiceGenerator.isRepareeItemForRepairedItem(annual, monthly));
+ }
+
+ /********************************************* isRepareeIncludedInRepair logic ********************************/
+
+ // Check for an item whose start and endDate exactly fit in repair item
+ @Test(groups = "fast")
+ public void testIsRepareeIncludedInRepair1() throws Exception {
+ final LocalDate startDate = new LocalDate(2012, 5, 1);
+ final LocalDate startRepair = new LocalDate(2012, 8, 1);
+ final LocalDate endDate = new LocalDate(2013, 5, 1);
+
+ // Repaired item
+ final InvoiceItem repairedItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, BigDecimal.TEN, BigDecimal.TEN, currency);
+
+ final InvoiceItem repairItem = new RepairAdjInvoiceItem(invoiceId, accountId, startRepair, endDate, BigDecimal.ONE, currency, repairedItem.getId());
+
+ final InvoiceItem invoiceItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startRepair, endDate, BigDecimal.TEN, BigDecimal.TEN, currency);
+ assertTrue(defaultInvoiceGenerator.isRepareeIncludedInRepair(repairItem, repairedItem.getSubscriptionId(), invoiceItem));
+ }
+
+ // Check for an item whose start is greater than repair startDate
+ @Test(groups = "fast")
+ public void testIsRepareeIncludedInRepair2() throws Exception {
+ final LocalDate startDate = new LocalDate(2012, 5, 1);
+ final LocalDate startRepair = new LocalDate(2012, 8, 1);
+ final LocalDate endDate = new LocalDate(2013, 5, 1);
+
+ // Repaired item
+ final InvoiceItem repairedItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, BigDecimal.TEN, BigDecimal.TEN, currency);
+
+ final InvoiceItem repairItem = new RepairAdjInvoiceItem(invoiceId, accountId, startRepair, endDate, BigDecimal.ONE, currency, repairedItem.getId());
+
+ final InvoiceItem invoiceItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startRepair.plusDays(1), endDate, BigDecimal.TEN, BigDecimal.TEN, currency);
+ assertTrue(defaultInvoiceGenerator.isRepareeIncludedInRepair(repairItem, repairedItem.getSubscriptionId(), invoiceItem));
+ }
+
+ // Check for an item whose endDate is lower than repair endDate
+ @Test(groups = "fast")
+ public void testIsRepareeIncludedInRepair3() throws Exception {
+ final LocalDate startDate = new LocalDate(2012, 5, 1);
+ final LocalDate startRepair = new LocalDate(2012, 8, 1);
+ final LocalDate endDate = new LocalDate(2013, 5, 1);
+
+ // Repaired item
+ final InvoiceItem repairedItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, BigDecimal.TEN, BigDecimal.TEN, currency);
+
+ final InvoiceItem repairItem = new RepairAdjInvoiceItem(invoiceId, accountId, startRepair, endDate, BigDecimal.ONE, currency, repairedItem.getId());
+
+ final InvoiceItem invoiceItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startRepair, endDate.minusDays(1), BigDecimal.TEN, BigDecimal.TEN, currency);
+ assertTrue(defaultInvoiceGenerator.isRepareeIncludedInRepair(repairItem, repairedItem.getSubscriptionId(), invoiceItem));
+ }
+
+ // Check for an item whose endDate is lower than repair endDate
+ @Test(groups = "fast")
+ public void testIsRepareeIncludedInRepair4() throws Exception {
+ final LocalDate startDate = new LocalDate(2012, 5, 1);
+ final LocalDate startRepair = new LocalDate(2012, 8, 1);
+ final LocalDate endDate = new LocalDate(2013, 5, 1);
+
+ // Repaired item
+ final InvoiceItem repairedItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, BigDecimal.TEN, BigDecimal.TEN, currency);
+
+ final InvoiceItem repairItem = new RepairAdjInvoiceItem(invoiceId, accountId, startRepair, endDate, BigDecimal.ONE, currency, repairedItem.getId());
+
+ final InvoiceItem invoiceItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startRepair, endDate.minusDays(1), BigDecimal.TEN, BigDecimal.TEN, currency);
+ assertTrue(defaultInvoiceGenerator.isRepareeIncludedInRepair(repairItem, repairedItem.getSubscriptionId(), invoiceItem));
+ }
+
+ // Check for an item whose endDate is greater than repair endDate
+ @Test(groups = "fast")
+ public void testIsRepareeIncludedInRepair5() throws Exception {
+ final LocalDate startDate = new LocalDate(2012, 5, 1);
+ final LocalDate startRepair = new LocalDate(2012, 8, 1);
+ final LocalDate endDate = new LocalDate(2013, 5, 1);
+
+ // Repaired item
+ final InvoiceItem repairedItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, BigDecimal.TEN, BigDecimal.TEN, currency);
+
+ final InvoiceItem repairItem = new RepairAdjInvoiceItem(invoiceId, accountId, startRepair, endDate, BigDecimal.ONE, currency, repairedItem.getId());
+
+ final InvoiceItem invoiceItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startRepair, endDate.plusDays(1), BigDecimal.TEN, BigDecimal.TEN, currency);
+ assertFalse(defaultInvoiceGenerator.isRepareeIncludedInRepair(repairItem, repairedItem.getSubscriptionId(), invoiceItem));
+ }
+
+ @Test(groups = "fast")
+ public void testIsRepareeIncludedInRepairWrongSubscription() throws Exception {
+ final LocalDate startDate = new LocalDate(2012, 5, 1);
+ final LocalDate startRepair = new LocalDate(2012, 8, 1);
+ final LocalDate endDate = new LocalDate(2013, 5, 1);
+
+ // Repaired item
+ final InvoiceItem repairedItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, BigDecimal.TEN, BigDecimal.TEN, currency);
+
+ final InvoiceItem repairItem = new RepairAdjInvoiceItem(invoiceId, accountId, startRepair, endDate, BigDecimal.ONE, currency, repairedItem.getId());
+
+ final UUID otherSubscriptionId = UUID.fromString("a9cbee45-5796-4dc5-be1f-7c020518460d");
+ 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 8d12312..5d02e64 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
@@ -22,6 +22,7 @@ import java.util.List;
import java.util.UUID;
import org.joda.time.LocalDate;
+import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.ning.billing.catalog.api.Currency;
@@ -46,6 +47,13 @@ public class TestDefaultInvoiceGeneratorUnit extends InvoiceTestSuiteNoDB {
private final String phaseName = "my-phase";
private final Currency currency = Currency.USD;
+ private DefaultInvoiceGenerator defaultInvoiceGenerator;
+
+ @BeforeClass(groups = "fast")
+ protected void beforeClass() throws Exception {
+ super.beforeClass();
+ this.defaultInvoiceGenerator = (DefaultInvoiceGenerator) generator;
+ }
@Test(groups = "fast")
public void testRemoveCancellingInvoiceItemsFixedPrice() {
@@ -61,7 +69,7 @@ public class TestDefaultInvoiceGeneratorUnit extends InvoiceTestSuiteNoDB {
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) generator).removeRepairedAndRepairInvoiceItems(items, new LinkedList<InvoiceItem>());
+ defaultInvoiceGenerator.removeRepairedAndRepairInvoiceItems(items, new LinkedList<InvoiceItem>());
assertEquals(items.size(), 1);
final InvoiceItem leftItem = items.get(0);
assertEquals(leftItem.getInvoiceItemType(), InvoiceItemType.RECURRING);
@@ -83,7 +91,7 @@ public class TestDefaultInvoiceGeneratorUnit extends InvoiceTestSuiteNoDB {
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) generator).removeRepairedAndRepairInvoiceItems(items, new LinkedList<InvoiceItem>());
+ defaultInvoiceGenerator.removeRepairedAndRepairInvoiceItems(items, new LinkedList<InvoiceItem>());
assertEquals(items.size(), 1);
final InvoiceItem leftItem = items.get(0);
assertEquals(leftItem.getInvoiceItemType(), InvoiceItemType.RECURRING);
@@ -107,7 +115,7 @@ public class TestDefaultInvoiceGeneratorUnit extends InvoiceTestSuiteNoDB {
final InvoiceItem other1 = new FixedPriceInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, amount, currency);
proposed.add(other1);
- ((DefaultInvoiceGenerator) generator).removeMatchingInvoiceItems(proposed, existing);
+ defaultInvoiceGenerator.removeMatchingInvoiceItems(proposed, existing);
assertEquals(existing.size(), 1);
assertEquals(proposed.size(), 0);
}
@@ -129,7 +137,7 @@ public class TestDefaultInvoiceGeneratorUnit extends InvoiceTestSuiteNoDB {
proposed.add(other1);
proposed.add(other2);
- ((DefaultInvoiceGenerator) generator).removeMatchingInvoiceItems(proposed, existing);
+ defaultInvoiceGenerator.removeMatchingInvoiceItems(proposed, existing);
assertEquals(existing.size(), 0);
assertEquals(proposed.size(), 1);
}
@@ -156,7 +164,7 @@ public class TestDefaultInvoiceGeneratorUnit extends InvoiceTestSuiteNoDB {
proposed.add(other1);
proposed.add(other2);
- ((DefaultInvoiceGenerator) generator).removeMatchingInvoiceItems(proposed, existing);
+ defaultInvoiceGenerator.removeMatchingInvoiceItems(proposed, existing);
assertEquals(existing.size(), 0);
assertEquals(proposed.size(), 1);
}
@@ -183,7 +191,7 @@ public class TestDefaultInvoiceGeneratorUnit extends InvoiceTestSuiteNoDB {
proposed.add(item1);
proposed.add(other);
- ((DefaultInvoiceGenerator) generator).removeMatchingInvoiceItems(proposed, existing);
+ defaultInvoiceGenerator.removeMatchingInvoiceItems(proposed, existing);
assertEquals(existing.size(), 0);
assertEquals(proposed.size(), 1);
final InvoiceItem leftItem = proposed.get(0);
@@ -212,7 +220,7 @@ public class TestDefaultInvoiceGeneratorUnit extends InvoiceTestSuiteNoDB {
proposed.add(item1);
proposed.add(other);
- ((DefaultInvoiceGenerator) generator).removeMatchingInvoiceItems(proposed, existing);
+ defaultInvoiceGenerator.removeMatchingInvoiceItems(proposed, existing);
assertEquals(existing.size(), 0);
assertEquals(proposed.size(), 1);
final InvoiceItem leftItem = proposed.get(0);
@@ -220,76 +228,5 @@ public class TestDefaultInvoiceGeneratorUnit extends InvoiceTestSuiteNoDB {
assertEquals(leftItem.getAmount(), amount2);
}
- // STEPH same as testRemoveCancellingInvoiceItemsFixedPrice: should we have one for FixedPrice?
- @Test(groups = "fast")
- public void testAddRepairedItemsItemsRecurringPrice() {
- 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 UUID firstInvoiceId = UUID.randomUUID();
- final List<InvoiceItem> existing = new LinkedList<InvoiceItem>();
- final InvoiceItem item1 = new RecurringInvoiceItem(firstInvoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount1, rate1, currency);
- existing.add(item1);
-
- final List<InvoiceItem> proposed = new LinkedList<InvoiceItem>();
- final InvoiceItem other = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, endDate, nextEndDate, amount2, rate2, currency);
- proposed.add(other);
-
- ((DefaultInvoiceGenerator) generator).addRepairItems(existing, proposed);
- assertEquals(existing.size(), 1);
- assertEquals(proposed.size(), 2);
- final InvoiceItem leftItem1 = proposed.get(0);
- assertEquals(leftItem1.getInvoiceId(), invoiceId);
- assertEquals(leftItem1.getInvoiceItemType(), InvoiceItemType.RECURRING);
- assertEquals(leftItem1.getAmount(), amount2);
-
- final InvoiceItem newItem2 = proposed.get(1);
- assertEquals(newItem2.getInvoiceId(), firstInvoiceId);
- assertEquals(newItem2.getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
- assertEquals(newItem2.getAmount(), item1.getAmount().negate());
- assertEquals(newItem2.getLinkedItemId(), item1.getId());
- }
-
- @Test(groups = "fast")
- public void testShouldFindRepareeForPartialRepairs() throws Exception {
- final LocalDate startDate = new LocalDate(2012, 5, 1);
- final LocalDate endDate = new LocalDate(2012, 6, 1);
- // Repaired item
- final InvoiceItem silver = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, BigDecimal.TEN, BigDecimal.TEN, currency);
-
- // Reparee item
- final LocalDate actualEndDateSilver = new LocalDate(2012, 5, 10);
- final InvoiceItem actualSilver = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, actualEndDateSilver, new BigDecimal("3"), BigDecimal.TEN, currency);
-
- // New item
- final InvoiceItem gold = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, "new-" + planName, phaseName, actualEndDateSilver, endDate, BigDecimal.TEN, new BigDecimal("15"), currency);
-
- assertFalse(((DefaultInvoiceGenerator) generator).isRepareeItemForRepairedItem(silver, silver));
- assertFalse(((DefaultInvoiceGenerator) generator).isRepareeItemForRepairedItem(silver, gold));
- assertTrue(((DefaultInvoiceGenerator) generator).isRepareeItemForRepairedItem(silver, actualSilver));
- }
-
- @Test(groups = "fast")
- public void testShouldntFindRepareeForFullRepairs() throws Exception {
- final LocalDate startDate = new LocalDate(2012, 5, 1);
- final LocalDate endDate = new LocalDate(2013, 5, 1);
- // Repaired item
- final InvoiceItem annual = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, BigDecimal.TEN, BigDecimal.TEN, currency);
- // There is no reparee - full repair
-
- // New item
- final LocalDate endDate2 = new LocalDate(2012, 6, 1);
- final InvoiceItem monthly = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, "new-" + planName, phaseName, startDate, endDate2, BigDecimal.TEN, BigDecimal.TEN, currency);
-
- assertFalse(((DefaultInvoiceGenerator) generator).isRepareeItemForRepairedItem(annual, annual));
- assertFalse(((DefaultInvoiceGenerator) generator).isRepareeItemForRepairedItem(annual, monthly));
- }
-}
+}
\ No newline at end of file