killbill-memoizeit

Fix issue with nextBillingDate notification for annual plans

10/12/2012 9:52:01 PM

Details

diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
index 6371c98..8f21dfa 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
@@ -16,6 +16,10 @@
 
 package com.ning.billing.beatrix.integration;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
@@ -41,10 +45,6 @@ import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItemType;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-
 @Guice(modules = {BeatrixModule.class})
 public class TestIntegration extends TestIntegrationBase {
 
@@ -116,10 +116,10 @@ public class TestIntegration extends TestIntegrationBase {
         //
         // MOVE 4 * TIME THE CLOCK
         //
-        setDateAndCheckForCompletion(new DateTime(2012, 2, 28, 0, 3, 45, 0, testTimeZone));
-        setDateAndCheckForCompletion(new DateTime(2012, 2, 29, 0, 3, 45, 0, testTimeZone));
-        setDateAndCheckForCompletion(new DateTime(2012, 3, 1, 0, 3, 45, 0, testTimeZone));
-        setDateAndCheckForCompletion(new DateTime(2012, 3, 2, 0, 3, 45, 0, testTimeZone), NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
+        setDateAndCheckForCompletion(new DateTime(2012, 2, 28, 23, 59, 59, 0, testTimeZone));
+        setDateAndCheckForCompletion(new DateTime(2012, 2, 29, 23, 59, 59, 0, testTimeZone));
+        setDateAndCheckForCompletion(new DateTime(2012, 3, 1, 23, 59, 59, 0, testTimeZone));
+        setDateAndCheckForCompletion(new DateTime(2012, 3, 2, 23, 59, 59, 0, testTimeZone), NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
         invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 3, 2),
                                                                                                new LocalDate(2012, 3, 31), InvoiceItemType.RECURRING, new BigDecimal("561.25")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 3, 31), callContext);
@@ -198,10 +198,10 @@ public class TestIntegration extends TestIntegrationBase {
         //
         // MOVE 4 * TIME THE CLOCK
         //
-        setDateAndCheckForCompletion(new DateTime(2012, 2, 28, 0, 3, 45, 0, testTimeZone));
-        setDateAndCheckForCompletion(new DateTime(2012, 2, 29, 0, 3, 45, 0, testTimeZone));
-        setDateAndCheckForCompletion(new DateTime(2012, 3, 1, 0, 3, 45, 0, testTimeZone));
-        setDateAndCheckForCompletion(new DateTime(2012, 3, 2, 0, 3, 45, 0, testTimeZone), NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
+        setDateAndCheckForCompletion(new DateTime(2012, 2, 28, 23, 59, 59, 0, testTimeZone));
+        setDateAndCheckForCompletion(new DateTime(2012, 2, 29, 23, 59, 59, 0, testTimeZone));
+        setDateAndCheckForCompletion(new DateTime(2012, 3, 1, 23, 59, 59, 0, testTimeZone));
+        setDateAndCheckForCompletion(new DateTime(2012, 3, 2, 23, 59, 59, 0, testTimeZone), NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
         invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 3, 2),
                                                                                                new LocalDate(2012, 4, 2), InvoiceItemType.RECURRING, new BigDecimal("599.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 4, 2), callContext);
@@ -280,16 +280,16 @@ public class TestIntegration extends TestIntegrationBase {
         //
         // MOVE 4 * TIME THE CLOCK
         //
-        setDateAndCheckForCompletion(new DateTime(2012, 2, 28, 0, 3, 45, 0, testTimeZone));
-        setDateAndCheckForCompletion(new DateTime(2012, 2, 29, 0, 3, 45, 0, testTimeZone));
-        setDateAndCheckForCompletion(new DateTime(2012, 3, 1, 0, 3, 45, 0, testTimeZone));
-        setDateAndCheckForCompletion(new DateTime(2012, 3, 2, 0, 3, 45, 0, testTimeZone), NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
+        setDateAndCheckForCompletion(new DateTime(2012, 2, 28,  23, 59, 59, 0, testTimeZone));
+        setDateAndCheckForCompletion(new DateTime(2012, 2, 29,  23, 59, 59, 0, testTimeZone));
+        setDateAndCheckForCompletion(new DateTime(2012, 3, 1,  23, 59, 59, 0, testTimeZone));
+        setDateAndCheckForCompletion(new DateTime(2012, 3, 2,  23, 59, 59, 0, testTimeZone), NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
         // PRO_RATION
         invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 3, 2),
                                                                                                new LocalDate(2012, 3, 3), InvoiceItemType.RECURRING, new BigDecimal("20.70")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 3, 3), callContext);
 
-        setDateAndCheckForCompletion(new DateTime(2012, 3, 3, 0, 3, 45, 0, testTimeZone), NextEvent.INVOICE, NextEvent.PAYMENT);
+        setDateAndCheckForCompletion(new DateTime(2012, 3, 3, 23, 59, 59, 0, testTimeZone), NextEvent.INVOICE, NextEvent.PAYMENT);
         invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedItemCheck(new LocalDate(2012, 3, 3),
                                                                                                new LocalDate(2012, 4, 3), InvoiceItemType.RECURRING, new BigDecimal("599.95")));
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 4, 3), callContext);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/AuditedInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/AuditedInvoiceDao.java
index 90f071d..ec27c0c 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/AuditedInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/AuditedInvoiceDao.java
@@ -25,7 +25,6 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
-import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
@@ -44,7 +43,6 @@ import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.api.InvoicePayment.InvoicePaymentType;
 import com.ning.billing.invoice.api.user.DefaultInvoiceAdjustmentEvent;
-import com.ning.billing.invoice.generator.InvoiceDateUtils;
 import com.ning.billing.invoice.model.CreditAdjInvoiceItem;
 import com.ning.billing.invoice.model.CreditBalanceAdjInvoiceItem;
 import com.ning.billing.invoice.model.DefaultInvoice;
@@ -206,7 +204,7 @@ public class AuditedInvoiceDao implements InvoiceDao {
                     audits.addAll(createAudits(TableName.INVOICE_ITEMS, recordIdList));
 
                     final List<InvoiceItem> recurringInvoiceItems = invoice.getInvoiceItems(RecurringInvoiceItem.class);
-                    notifyOfFutureBillingEvents(transactional, invoice.getAccountId(), billCycleDayUTC, recurringInvoiceItems);
+                    notifyOfFutureBillingEvents(transactional, invoice.getAccountId(), recurringInvoiceItems);
 
                     final List<InvoicePayment> invoicePayments = invoice.getPayments();
                     final InvoicePaymentSqlDao invoicePaymentSqlDao = transactional.become(InvoicePaymentSqlDao.class);
@@ -948,31 +946,22 @@ public class AuditedInvoiceDao implements InvoiceDao {
         invoice.addPayments(invoicePayments);
     }
 
-    private void notifyOfFutureBillingEvents(final InvoiceSqlDao dao, final UUID accountId, final int billCycleDayUTC, final List<InvoiceItem> invoiceItems) {
-        UUID subscriptionForNextNotification = null;
-        boolean shouldBeNotified = false;
+    private void notifyOfFutureBillingEvents(final InvoiceSqlDao dao, final UUID accountId, final List<InvoiceItem> invoiceItems) {
+
         for (final InvoiceItem item : invoiceItems) {
             if (item.getInvoiceItemType() == InvoiceItemType.RECURRING) {
                 final RecurringInvoiceItem recurringInvoiceItem = (RecurringInvoiceItem) item;
                 if ((recurringInvoiceItem.getEndDate() != null) &&
                         (recurringInvoiceItem.getAmount() == null ||
                         recurringInvoiceItem.getAmount().compareTo(BigDecimal.ZERO) >= 0)) {
-                    subscriptionForNextNotification = recurringInvoiceItem.getSubscriptionId();
-                    shouldBeNotified = true;
-                    break;
+                    //
+                    // We insert a future notification for each recurring subscription at the end of the service period  = new CTD of the subscription
+                    //
+                    nextBillingDatePoster.insertNextBillingNotification(dao, accountId, recurringInvoiceItem.getSubscriptionId(),
+                            recurringInvoiceItem.getEndDate().toDateTimeAtCurrentTime());
                 }
             }
         }
-
-        // We only need to get notified on the BCD. For other invoice events (e.g. phase changes),
-        // we'll be notified by entitlement.
-        if (shouldBeNotified) {
-            // We could be notified at any time during the day at the billCycleDay - use the current time to
-            // spread the load.
-            final DateTime nextNotificationDateTime = InvoiceDateUtils.calculateBillingCycleDateAfter(clock.getUTCNow(), billCycleDayUTC);
-            // NextBillingDatePoster will ignore duplicates
-            nextBillingDatePoster.insertNextBillingNotification(dao, accountId, subscriptionForNextNotification, nextNotificationDateTime);
-        }
     }
 
     private void notifyBusOfInvoiceAdjustment(final Transmogrifier transactional, final UUID invoiceId, final UUID accountId,
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
index 5788d4b..54448e3 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
@@ -17,7 +17,6 @@
 package com.ning.billing.invoice.notification;
 
 import java.io.IOException;
-import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
@@ -30,7 +29,6 @@ import com.ning.billing.util.callcontext.CallOrigin;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.UserType;
-import com.ning.billing.util.notificationq.Notification;
 import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
@@ -62,13 +60,6 @@ public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
                                                                              DefaultNextBillingDateNotifier.NEXT_BILLING_DATE_NOTIFIER_QUEUE);
             log.info("Queuing next billing date notification. id: {}, timestamp: {}", subscriptionId.toString(), futureNotificationTime.toString());
 
-            final List<Notification> existingNotifications = nextBillingQueue.getNotificationForAccountAndDate(accountId, futureNotificationTime, context);
-            if (existingNotifications.size() > 0) {
-                log.info(String.format("%s : notification for account %s and date %s already exist, skip...",
-                                       DefaultNextBillingDateNotifier.NEXT_BILLING_DATE_NOTIFIER_QUEUE, accountId, futureNotificationTime));
-                return;
-            }
-
             nextBillingQueue.recordFutureNotificationFromTransaction(transactionalDao, futureNotificationTime, accountId,
                                                                      new NextBillingDateNotificationKey(subscriptionId), context);
         } catch (NoSuchNotificationQueue e) {