killbill-uncached

Add tests for more complex scenarii with multiple reparees/repairs

4/29/2013 9:16:53 PM

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 be1cbf9..c947853 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
@@ -187,6 +187,183 @@ public class TestOverdueIntegration extends TestOverdueBase {
         assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(new BigDecimal("-11.79")), 0);
     }
 
+
+
+
+    // We set the the property killbill.payment.retry.days=8,8,8,8,8,8,8,8 so that Payment retry logic does not end with an ABORTED state
+    // preventing final instant payment to succeed.
+    @Test(groups = "slow")
+    public void testSingleRepareeOnOverdueState() throws Exception {
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+        // Set next invoice to fail and create subscription
+        paymentPlugin.makeAllInvoicesFailWithError(true);
+        final Subscription baseSubscription = createSubscriptionAndCheckForCompletion(bundle.getId(), productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 5, 1), callContext);
+
+        // 2012, 5, 31 => DAY 30 have to get out of trial {I0, P0}
+        addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
+
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 6, 30), callContext);
+
+        // 2012, 6, 8 => Retry P0
+        addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR);
+        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+
+        // 2012, 6, 16 => Retry P0
+        addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR);
+        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+
+        // 2012, 6, 24 => Retry P0
+        addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR);
+        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+
+        // 2012, 6, 31 => P1 (We se 6/31 instead of 6/30 because invoice might happen later in that day)
+        addDaysAndCheckForCompletion(7, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
+        checkODState("OD1");
+        checkChangePlanWithOverdueState(baseSubscription, true);
+        invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 7, 31), callContext);
+
+        // 2012, 7, 2 => Retry P0
+        addDaysAndCheckForCompletion(1, NextEvent.PAYMENT_ERROR);
+        checkODState("OD1");
+
+        // 2012, 7, 9 => Retry P1
+        addDaysAndCheckForCompletion(7, NextEvent.PAYMENT_ERROR);
+        checkODState("OD1");
+
+        // 2012, 7, 10 => Retry P0
+        addDaysAndCheckForCompletion(1, NextEvent.PAYMENT_ERROR);
+        checkODState("OD2");
+
+        // 2012, 7, 17 => Retry P1
+        addDaysAndCheckForCompletion(7, NextEvent.PAYMENT_ERROR);
+        checkODState("OD2");
+
+        // 2012, 7, 18 => Retry P0
+        addDaysAndCheckForCompletion(1, NextEvent.PAYMENT_ERROR);
+        checkODState("OD2");
+
+        // 2012, 7, 23 => Should be 20 but notficationQ event occurs on 23...
+        addDaysAndCheckForCompletion(5);
+        checkODState("OD3");
+
+
+        paymentPlugin.makeAllInvoicesFailWithError(false);
+        final Collection<Invoice> invoices = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
+        for (final Invoice invoice : invoices) {
+            if (invoice.getBalance().compareTo(BigDecimal.ZERO) > 0) {
+                createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT);
+            }
+        }
+        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+
+
+        // Add 10 days to generate next invoice
+        addDaysAndCheckForCompletion(10, NextEvent.INVOICE,  NextEvent.INVOICE_ADJUSTMENT, NextEvent.PAYMENT);
+
+        invoiceChecker.checkRepairedInvoice(account.getId(), 3,
+                                            callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
+                                            // We paid up to 07-31, hence the adjustment
+                                            new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 10), new LocalDate(2012, 7, 23), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-102.13")),
+                                            new ExpectedInvoiceItemCheck(new LocalDate(2012, 8, 2), new LocalDate(2012, 8, 2), InvoiceItemType.CBA_ADJ, new BigDecimal("102.13")));
+
+        invoiceChecker.checkInvoice(account.getId(), 4, callContext,
+                                    // Item for the upgraded recurring plan
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2012, 8, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
+                                    // Credits consumed
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 8, 2), new LocalDate(2012, 8, 2), InvoiceItemType.CBA_ADJ, new BigDecimal("-102.13")));
+        invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 8, 31), callContext);
+
+        // Verify the account balance: 249.95 - 74.99 - 154.85
+        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
+    }
+
+
+    // We set the the property killbill.payment.retry.days=8,8,8,8,8,8,8,8 so that Payment retry logic does not end with an ABORTED state
+    // preventing final instant payment to succeed.
+    @Test(groups = "slow")
+    public void testMultipleRepareeOnOverdueState() throws Exception {
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+        // Set next invoice to fail and create subscription
+        paymentPlugin.makeAllInvoicesFailWithError(true);
+        final Subscription baseSubscription = createSubscriptionAndCheckForCompletion(bundle.getId(), productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 5, 1), callContext);
+
+        // 2012, 5, 31 => DAY 30 have to get out of trial {I0, P0}
+        addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
+
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2013, 5, 31), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
+        invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2013, 5, 31), callContext);
+
+        // 2012, 6, 8 => Retry P0
+        addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR);
+        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+
+        // 2012, 6, 16 => Retry P0
+        addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR);
+        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+
+        // 2012, 6, 24 => Retry P0
+        addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR);
+        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+
+        // 2012, 7, 2 => Retry P0
+        addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR);
+        checkODState("OD1");
+
+        // 2012, 7, 10 => Retry P0
+        addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR);
+        checkODState("OD2");
+
+        // 2012, 7, 18 => Retry P0
+        addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR);
+        checkODState("OD2");
+
+        // 2012, 7, 23 => Should be 20 but notficationQ event occurs on 23...
+        addDaysAndCheckForCompletion(5);
+        checkODState("OD3");
+
+
+        paymentPlugin.makeAllInvoicesFailWithError(false);
+        final Collection<Invoice> invoices = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
+        for (final Invoice invoice : invoices) {
+            if (invoice.getBalance().compareTo(BigDecimal.ZERO) > 0) {
+                createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT);
+            }
+        }
+        checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+
+        // Move to 2012, 7, 31 and Make a change of plan
+        addDaysAndCheckForCompletion(8);
+
+        checkChangePlanWithOverdueState(baseSubscription, false);
+
+
+        invoiceChecker.checkRepairedInvoice(account.getId(), 2,
+                                            callContext, 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.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, 31), new LocalDate(2012, 7, 31), InvoiceItemType.CBA_ADJ, new BigDecimal("2084.36")));
+
+        invoiceChecker.checkInvoice(account.getId(), 3, callContext,
+                                    // Item for the upgraded recurring plan
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2012, 8, 31), InvoiceItemType.RECURRING, new BigDecimal("599.95")),
+                                    // Credits consumed
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2012, 7, 31), InvoiceItemType.CBA_ADJ, new BigDecimal("-599.95")));
+        invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 8, 31), callContext);
+
+        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(new BigDecimal("-1484.41")), 0);
+    }
+
+
     @Test(groups = "slow")
     public void testOverdueStateIfNoPaymentMethod() throws Exception {
         // This test is similar to the previous one - but there is no default payment method on the account, so there
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 9cde6fa..b690e7d 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
@@ -16,15 +16,19 @@
 
 package com.ning.billing.invoice.generator;
 
+import java.awt.image.DataBufferUShort;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
 
+import org.joda.time.Days;
 import org.joda.time.LocalDate;
 import org.joda.time.Months;
 import org.slf4j.Logger;
@@ -177,34 +181,76 @@ 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) {
-        InvoiceItem repareeItem = null;
+
+
+        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
+        BigDecimal totalRepareeItemAmount = candidateRepairItem.getAmount();
+        final List<InvoiceItem> reparees = new ArrayList<InvoiceItem>();
         for (final InvoiceItem cur : proposedItems) {
             if (isRepareeItemForRepairedItem(repairedItem, cur)) {
-                if (repareeItem == null) {
-                    repareeItem = cur;
-                } else {
-                    log.warn("Found multiple reparee item for repaired invoice item " + repairedItem.getId());
-                }
+                nbTotalRepaireeDays += Days.daysBetween(cur.getStartDate(), cur.getEndDate()).getDays();
+                reparees.add(cur);
+                totalRepareeItemAmount = totalRepareeItemAmount.add(cur.getAmount());
             }
         }
+        int nbTotalRepairedDays = Days.daysBetween(candidateRepairItem.getStartDate(), candidateRepairItem.getEndDate()).getDays() - nbTotalRepaireeDays;
+
         // If we repaired the full period there is no repairee item
-        if (repareeItem == null) {
+        if (reparees.size() == 0) {
             proposedItems.add(candidateRepairItem);
             return;
         }
 
-        final BigDecimal partialRepairAmount = candidateRepairItem.getAmount().add(repareeItem.getAmount());
-        if (partialRepairAmount.compareTo(BigDecimal.ZERO) < 0) {
-            final RepairAdjInvoiceItem repairItem = new RepairAdjInvoiceItem(candidateRepairItem.getInvoiceId(), candidateRepairItem.getAccountId(), repareeItem.getEndDate(), candidateRepairItem.getEndDate(), partialRepairAmount, candidateRepairItem.getCurrency(), candidateRepairItem.getLinkedItemId());
-            proposedItems.remove(repareeItem);
-            proposedItems.add(repairItem);
+        // Sort the reparees based on startDate in order to create the repair items -- based on the endDate (previous repairee) -> startDate (next reparee)
+        Collections.sort(reparees, new Comparator<InvoiceItem>() {
+            @Override
+            public int compare(final InvoiceItem o1, final InvoiceItem o2) {
+                return o1.getStartDate().compareTo(o2.getStartDate());
+            }
+        });
+
+        //Build the reparees
+        BigDecimal totalRepairItemAmount = BigDecimal.ZERO;
+        List<InvoiceItem> repairedItems = new ArrayList<InvoiceItem>();
+        InvoiceItem prevReparee = null;
+        final Iterator<InvoiceItem> it = reparees.iterator();
+        while (it.hasNext()) {
+            final InvoiceItem nextReparee = it.next();
+            if (prevReparee != null) {
+                // 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) :
+                                                    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());
+                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);
+            final RepairAdjInvoiceItem repairItem = new RepairAdjInvoiceItem(candidateRepairItem.getInvoiceId(), candidateRepairItem.getAccountId(), prevReparee.getEndDate(), candidateRepairItem.getEndDate(), repairItemAmount, candidateRepairItem.getCurrency(), repairedItem.getId());
+            repairedItems.add(repairItem);
+        }
+
+        // Finally remove all reparees from the proposed items and add all repaired items in the invoice
+        for (InvoiceItem reparee : reparees) {
+            proposedItems.remove(reparee);
+        }
+        proposedItems.addAll(repairedItems);
     }
 
     /**
@@ -221,13 +267,13 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
                // We assume the items are correctly created, so that the subscription id check implicitly
                // verifies that account id and bundle id matches
                repairedInvoiceItem.getSubscriptionId().equals(invoiceItem.getSubscriptionId()) &&
-               // The reparee item is the "portion used" of the repaired item, hence it will have the same start date
-               repairedInvoiceItem.getStartDate().compareTo(invoiceItem.getStartDate()) == 0 &&
+               // service period for reparee should be included in service period of repaired-- true for startDate and endDate
+               repairedInvoiceItem.getStartDate().compareTo(invoiceItem.getStartDate()) <= 0 &&
                // Similarly, check the "portion used" is less than the original service end date. The check
                // is strict, otherwise there wouldn't be anything to repair
                ((repairedInvoiceItem.getEndDate() == null && invoiceItem.getEndDate() == null) ||
                 (repairedInvoiceItem.getEndDate() != null && invoiceItem.getEndDate() != null &&
-                 repairedInvoiceItem.getEndDate().isAfter(invoiceItem.getEndDate()))) &&
+                 repairedInvoiceItem.getEndDate().compareTo(invoiceItem.getEndDate()) >= 0)) &&
                // Finally, for the tricky part... In case of complete repairs, the new item will always meet all of the
                // following conditions: same type, subscription, start date. Depending on the catalog configuration, the end
                // date check could also match (e.g. repair from annual to monthly). For that scenario, we need to default
diff --git a/invoice/src/main/java/com/ning/billing/invoice/generator/InvoiceDateUtils.java b/invoice/src/main/java/com/ning/billing/invoice/generator/InvoiceDateUtils.java
index 2b5f8dd..ff1591c 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/generator/InvoiceDateUtils.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/generator/InvoiceDateUtils.java
@@ -33,21 +33,41 @@ public class InvoiceDateUtils {
     private static final int ROUNDING_METHOD = InvoicingConfiguration.getRoundingMode();
     private static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
 
-    public static BigDecimal calculateProRationBeforeFirstBillingPeriod(final LocalDate startDate, final LocalDate nextBillingCycleDate,
-                                                                        final BillingPeriod billingPeriod) {
-        final LocalDate previousBillingCycleDate = nextBillingCycleDate.plusMonths(-billingPeriod.getNumberOfMonths());
 
+    /**
+     *
+     * Called internally to calculate proration or when we recalculate approximate repair amount
+     *
+     * @param startDate                 start date of the prorated interval
+     * @param endDate                   end date of the prorated interval
+     * @param previousBillingCycleDate  start date of the period
+     * @param nextBillingCycleDate      end date of the period
+     * @return
+     */
+    public static BigDecimal calculateProrationBetweenDates(final LocalDate startDate, final LocalDate endDate, final LocalDate previousBillingCycleDate, final LocalDate nextBillingCycleDate) {
         final int daysBetween = Days.daysBetween(previousBillingCycleDate, nextBillingCycleDate).getDays();
+        return calculateProrationBetweenDates(startDate, endDate, daysBetween);
+    }
+
+    public static BigDecimal calculateProrationBetweenDates(final LocalDate startDate, final LocalDate endDate, int daysBetween) {
         if (daysBetween <= 0) {
             return BigDecimal.ZERO;
         }
 
         final BigDecimal daysInPeriod = new BigDecimal(daysBetween);
-        final BigDecimal days = new BigDecimal(Days.daysBetween(startDate, nextBillingCycleDate).getDays());
+        final BigDecimal days = new BigDecimal(Days.daysBetween(startDate, endDate).getDays());
 
         return days.divide(daysInPeriod, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD);
     }
 
+
+    public static BigDecimal calculateProRationBeforeFirstBillingPeriod(final LocalDate startDate, final LocalDate nextBillingCycleDate,
+                                                                        final BillingPeriod billingPeriod) {
+        final LocalDate previousBillingCycleDate = nextBillingCycleDate.plusMonths(-billingPeriod.getNumberOfMonths());
+
+        return calculateProrationBetweenDates(startDate, nextBillingCycleDate, previousBillingCycleDate, nextBillingCycleDate);
+    }
+
     public static int calculateNumberOfWholeBillingPeriods(final LocalDate startDate, final LocalDate endDate, final BillingPeriod billingPeriod) {
         final int numberOfMonths = Months.monthsBetween(startDate, endDate).getMonths();
         final int numberOfMonthsInPeriod = billingPeriod.getNumberOfMonths();
@@ -129,15 +149,12 @@ public class InvoiceDateUtils {
         }
     }
 
+
     public static BigDecimal calculateProRationAfterLastBillingCycleDate(final LocalDate endDate, final LocalDate previousBillThroughDate,
                                                                          final BillingPeriod billingPeriod) {
         // Note: assumption is that previousBillThroughDate is correctly aligned with the billing cycle day
         final LocalDate nextBillThroughDate = previousBillThroughDate.plusMonths(billingPeriod.getNumberOfMonths());
-        final BigDecimal daysInPeriod = new BigDecimal(Days.daysBetween(previousBillThroughDate, nextBillThroughDate).getDays());
-
-        final BigDecimal days = new BigDecimal(Days.daysBetween(previousBillThroughDate, endDate).getDays());
-
-        return days.divide(daysInPeriod, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD);
+        return calculateProrationBetweenDates(previousBillThroughDate, endDate, previousBillThroughDate, nextBillThroughDate);
     }
 
  /*
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/TestExternalPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/TestExternalPaymentProviderPlugin.java
index 71929c7..e1eaec2 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/TestExternalPaymentProviderPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/TestExternalPaymentProviderPlugin.java
@@ -57,8 +57,6 @@ public class TestExternalPaymentProviderPlugin extends PaymentTestSuiteNoDB {
         final PaymentInfoPlugin paymentInfoPlugin = plugin.processPayment(accountId, paymentId, paymentMethodId, amount, Currency.BRL, callContext);
 
         Assert.assertEquals(paymentInfoPlugin.getAmount(), amount);
-        Assert.assertEquals(Seconds.secondsBetween(paymentInfoPlugin.getCreatedDate(), clock.getUTCNow()).getSeconds(), 0);
-        Assert.assertEquals(Seconds.secondsBetween(paymentInfoPlugin.getEffectiveDate(), clock.getUTCNow()).getSeconds(), 0);
         Assert.assertNull(paymentInfoPlugin.getGatewayError());
         Assert.assertNull(paymentInfoPlugin.getGatewayErrorCode());
         Assert.assertEquals(paymentInfoPlugin.getStatus(), PaymentPluginStatus.PROCESSED);