Details
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index 504dafe..e6c094e 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -664,6 +664,26 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
final BillingPeriod billingPeriod,
final List<PlanPhasePriceOverride> overrides,
final NextEvent... events) {
+ return createBaseEntitlementWithPriceOverrideAndCheckForCompletion(accountId,
+ bundleExternalKey,
+ productName,
+ productCategory,
+ billingPeriod,
+ overrides,
+ null,
+ PriceListSet.DEFAULT_PRICELIST_NAME,
+ events);
+ }
+
+ protected DefaultEntitlement createBaseEntitlementWithPriceOverrideAndCheckForCompletion(final UUID accountId,
+ final String bundleExternalKey,
+ final String productName,
+ final ProductCategory productCategory,
+ final BillingPeriod billingPeriod,
+ final List<PlanPhasePriceOverride> overrides,
+ final LocalDate billingEffectiveDate,
+ final String priceList,
+ final NextEvent... events) {
if (productCategory == ProductCategory.ADD_ON) {
throw new RuntimeException("Unxepected Call for creating ADD_ON");
}
@@ -672,8 +692,8 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
@Override
public Entitlement apply(@Nullable final Void dontcare) {
try {
- final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);
- final UUID entitlementId = entitlementApi.createBaseEntitlement(accountId, spec, bundleExternalKey, overrides, null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, billingPeriod, priceList, null);
+ final UUID entitlementId = entitlementApi.createBaseEntitlement(accountId, spec, bundleExternalKey, overrides, null, billingEffectiveDate, false, true, ImmutableList.<PluginProperty>of(), callContext);
assertNotNull(entitlementId);
return entitlementApi.getEntitlementForId(entitlementId, callContext);
} catch (final EntitlementApiException e) {
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java
index 7cbed63..ebb14a2 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java
@@ -70,7 +70,6 @@ public class TestInvoiceNotifications extends TestIntegrationBase {
addDaysAndCheckForCompletion(7, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
}
-
@Test(groups = "slow")
public void testInvoiceNotificationWithFutureSubscriptionEvents() throws Exception {
clock.setDay(new LocalDate(2018, 1, 31));
@@ -79,11 +78,9 @@ public class TestInvoiceNotifications extends TestIntegrationBase {
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
-
final LocalDate billingDate = new LocalDate(2018, 2, 28);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("pistol-monthly-notrial");
-
busHandler.pushExpectedEvents(NextEvent.BLOCK);
final UUID entitlementId = entitlementApi.createBaseEntitlement(account.getId(), spec, "bundleKey", null, null, billingDate, false, true, ImmutableList.<PluginProperty>of(), callContext);
busHandler.assertListenerStatus();
@@ -103,11 +100,45 @@ public class TestInvoiceNotifications extends TestIntegrationBase {
// Move to the notification before the start date => 2018, 3, 21
addDaysAndCheckForCompletion(21, NextEvent.INVOICE_NOTIFICATION);
-
// Move to the change date => 2018, 3, 28
addDaysAndCheckForCompletion(7, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.NULL_INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-
}
+ @Test(groups = "slow")
+ public void testInvoiceNotificationInThePast() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2012, 4, 1));
+ final AccountData accountData = getAccountData(1);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ // Future create the entitlement
+ // Note: we need to use a plan without a trial to verify the fix, because we don't send invoice notifications for $0 invoices
+ final DefaultEntitlement bpSubscription = createBaseEntitlementWithPriceOverrideAndCheckForCompletion(account.getId(),
+ "bundleKey",
+ "Pistol",
+ ProductCategory.BASE,
+ BillingPeriod.MONTHLY,
+ null,
+ new LocalDate(2012, 4, 2),
+ "notrial",
+ NextEvent.BLOCK);
+
+ // Move to subscription start date
+ addDaysAndCheckForCompletion(1, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+ // Move to notification date
+ addDaysAndCheckForCompletion(23, NextEvent.INVOICE_NOTIFICATION);
+
+ // Move to next invoice
+ addDaysAndCheckForCompletion(7, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+ // Move to notification date
+ addDaysAndCheckForCompletion(24, NextEvent.INVOICE_NOTIFICATION);
+
+ // Move to next invoice
+ addDaysAndCheckForCompletion(7, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ }
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index 5c26d11..10ced74 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -1006,16 +1006,20 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
final InternalCallContext internalCallContext) {
for (final LocalDate notificationDate : callbackDateTimePerSubscriptions.getNotificationsForTrigger().keySet()) {
final DateTime notificationDateTime = internalCallContext.toUTCDateTime(notificationDate);
- final Set<UUID> subscriptionIds = callbackDateTimePerSubscriptions.getNotificationsForTrigger().get(notificationDate);
- nextBillingDatePoster.insertNextBillingNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionIds, notificationDateTime, callbackDateTimePerSubscriptions.isRescheduled(), internalCallContext);
+ if (notificationDateTime.compareTo(internalCallContext.getCreatedDate()) > 0) {
+ final Set<UUID> subscriptionIds = callbackDateTimePerSubscriptions.getNotificationsForTrigger().get(notificationDate);
+ nextBillingDatePoster.insertNextBillingNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionIds, notificationDateTime, callbackDateTimePerSubscriptions.isRescheduled(), internalCallContext);
+ }
}
final long dryRunNotificationTime = invoiceConfig.getDryRunNotificationSchedule(internalCallContext).getMillis();
if (dryRunNotificationTime > 0) {
for (final LocalDate notificationDate : callbackDateTimePerSubscriptions.getNotificationsForDryRun().keySet()) {
final DateTime notificationDateTime = internalCallContext.toUTCDateTime(notificationDate);
- final Set<UUID> subscriptionIds = callbackDateTimePerSubscriptions.getNotificationsForDryRun().get(notificationDate);
- nextBillingDatePoster.insertNextBillingDryRunNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionIds, notificationDateTime, notificationDateTime.plusMillis((int) dryRunNotificationTime), internalCallContext);
+ if (notificationDateTime.compareTo(internalCallContext.getCreatedDate()) > 0) {
+ final Set<UUID> subscriptionIds = callbackDateTimePerSubscriptions.getNotificationsForDryRun().get(notificationDate);
+ nextBillingDatePoster.insertNextBillingDryRunNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionIds, notificationDateTime, notificationDateTime.plusMillis((int) dryRunNotificationTime), internalCallContext);
+ }
}
}
}