killbill-memoizeit

Fix issue in the generation of the next usage billing calculation

4/23/2015 8:41:43 PM

Details

diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/BillingIntervalDetail.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/BillingIntervalDetail.java
index 6809742..2d95f1f 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/BillingIntervalDetail.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/BillingIntervalDetail.java
@@ -54,7 +54,7 @@ public class BillingIntervalDetail {
     public LocalDate getFutureBillingDateFor(int nbPeriod) {
         final int numberOfMonthsPerBillingPeriod = billingPeriod.getNumberOfMonths();
         LocalDate proposedDate = firstBillingCycleDate.plusMonths((nbPeriod) * numberOfMonthsPerBillingPeriod);
-        return alignProposedBillCycleDate(proposedDate);
+        return alignProposedBillCycleDate(proposedDate, billingCycleDay);
     }
 
     public LocalDate getLastBillingCycleDate() {
@@ -83,7 +83,7 @@ public class BillingIntervalDetail {
         while (proposedDate.isBefore(startDate)) {
             proposedDate = proposedDate.plusMonths(numberOfMonthsInPeriod);
         }
-        firstBillingCycleDate = alignProposedBillCycleDate(proposedDate);
+        firstBillingCycleDate = alignProposedBillCycleDate(proposedDate, billingCycleDay);
     }
 
     private void calculateEffectiveEndDate() {
@@ -107,7 +107,7 @@ public class BillingIntervalDetail {
             proposedDate = firstBillingCycleDate.plusMonths(numberOfPeriods * numberOfMonthsInPeriod);
             numberOfPeriods += 1;
         }
-        proposedDate = alignProposedBillCycleDate(proposedDate);
+        proposedDate = alignProposedBillCycleDate(proposedDate, billingCycleDay);
 
         // The proposedDate is greater to our endDate => return it
         if (endDate != null && endDate.isBefore(proposedDate)) {
@@ -130,7 +130,7 @@ public class BillingIntervalDetail {
 
         // Our proposed date is billingCycleDate prior to the effectiveEndDate
         proposedDate = proposedDate.plusMonths(-billingPeriod.getNumberOfMonths());
-        proposedDate = alignProposedBillCycleDate(proposedDate);
+        proposedDate = alignProposedBillCycleDate(proposedDate, billingCycleDay);
 
         if (proposedDate.isBefore(firstBillingCycleDate)) {
             // Make sure not to go too far in the past
@@ -144,9 +144,8 @@ public class BillingIntervalDetail {
     //
     // We start from a billCycleDate
     //
-    private LocalDate alignProposedBillCycleDate(final LocalDate proposedDate) {
+    public static LocalDate alignProposedBillCycleDate(final LocalDate proposedDate, final int billingCycleDay) {
         final int lastDayOfMonth = proposedDate.dayOfMonth().getMaximumValue();
-
         int proposedBillCycleDate = proposedDate.getDayOfMonth();
         if (proposedBillCycleDate < billingCycleDay && billingCycleDay <= lastDayOfMonth) {
             proposedBillCycleDate = billingCycleDay;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
index 9b9274d..0cb0931 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -64,11 +64,13 @@ import org.killbill.billing.invoice.api.user.DefaultNullInvoiceEvent;
 import org.killbill.billing.invoice.dao.InvoiceDao;
 import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
 import org.killbill.billing.invoice.dao.InvoiceModelDao;
+import org.killbill.billing.invoice.generator.BillingIntervalDetail;
 import org.killbill.billing.invoice.generator.InvoiceGenerator;
 import org.killbill.billing.invoice.model.DefaultInvoice;
 import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
 import org.killbill.billing.invoice.model.InvoiceItemFactory;
 import org.killbill.billing.invoice.model.RecurringInvoiceItem;
+import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.junction.BillingEventSet;
 import org.killbill.billing.junction.BillingInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
@@ -288,7 +290,7 @@ public class InvoiceDispatcher {
                                                                                                        return new InvoiceItemModelDao(input);
                                                                                                    }
                                                                                                });
-                final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(invoiceItemModelDaos, billingEvents.getUsages(), dateAndTimeZoneContext);
+                final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(invoiceItemModelDaos, billingEvents, dateAndTimeZoneContext);
 
                 // We filter any zero amount for USAGE items prior we generate the invoice, which may leave us with an invoice with no items;
                 // we recompute the isRealInvoiceWithItems flag based on what is left (the call to invoice is still necessary to set the future notifications).
@@ -367,7 +369,7 @@ public class InvoiceDispatcher {
 
 
     @VisibleForTesting
-    FutureAccountNotifications createNextFutureNotificationDate(final Iterable<InvoiceItemModelDao> invoiceItems, final Map<String, Usage> knownUsages, final DateAndTimeZoneContext dateAndTimeZoneContext) {
+    FutureAccountNotifications createNextFutureNotificationDate(final Iterable<InvoiceItemModelDao> invoiceItems, final BillingEventSet billingEvents, final DateAndTimeZoneContext dateAndTimeZoneContext) {
 
         final Map<UUID, List<DateTime>> result = new HashMap<UUID, List<DateTime>>();
 
@@ -414,16 +416,25 @@ public class InvoiceDispatcher {
             final String usageName = parts[1];
             final LocalDate endDate = perSubscriptionUsage.get(key);
 
-            final DateTime subscriptionUsageCallbackDate = getNextUsageBillingDate(usageName, endDate, dateAndTimeZoneContext, knownUsages);
+            final DateTime subscriptionUsageCallbackDate = getNextUsageBillingDate(subscriptionId, usageName, endDate, dateAndTimeZoneContext, billingEvents);
             perSubscriptionCallback.add(subscriptionUsageCallbackDate);
         }
 
         return new FutureAccountNotifications(dateAndTimeZoneContext, result);
     }
 
-    private DateTime getNextUsageBillingDate(final String usageName, final LocalDate chargedThroughDate, final DateAndTimeZoneContext dateAndTimeZoneContext, final Map<String, Usage> knownUsages) {
-        final Usage usage = knownUsages.get(usageName);
-        final LocalDate nextCallbackUsageDate = (usage.getBillingMode() == BillingMode.IN_ARREAR) ? chargedThroughDate.plusMonths(usage.getBillingPeriod().getNumberOfMonths()) : chargedThroughDate;
+    private DateTime getNextUsageBillingDate(final UUID subscriptionId, final String usageName, final LocalDate chargedThroughDate, final DateAndTimeZoneContext dateAndTimeZoneContext, final BillingEventSet billingEvents) {
+
+
+        final Usage usage = billingEvents.getUsages().get(usageName);
+        final  BillingEvent billingEventSubscription = Iterables.tryFind(billingEvents, new Predicate<BillingEvent>() {
+            @Override
+            public boolean apply(@Nullable final BillingEvent input) {
+                return input.getSubscription().getId().equals(subscriptionId);
+            }
+        }).orNull();
+
+        final LocalDate nextCallbackUsageDate = (usage.getBillingMode() == BillingMode.IN_ARREAR) ? BillingIntervalDetail.alignProposedBillCycleDate(chargedThroughDate.plusMonths(usage.getBillingPeriod().getNumberOfMonths()), billingEventSubscription.getBillCycleDayLocal()) : chargedThroughDate;
         return dateAndTimeZoneContext.computeUTCDateTimeFromLocalDate(nextCallbackUsageDate);
     }