killbill-memoizeit
Changes
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithCatalogUpdate.java 92(+92 -0)
invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java 4(+3 -1)
Details
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithCatalogUpdate.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithCatalogUpdate.java
index c314ac2..582c2c5 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithCatalogUpdate.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithCatalogUpdate.java
@@ -33,6 +33,7 @@ import org.killbill.billing.api.TestApiListener.NextEvent;
import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
import org.killbill.billing.callcontext.DefaultCallContext;
import org.killbill.billing.catalog.DefaultPlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.catalog.api.CatalogUserApi;
@@ -48,6 +49,7 @@ import org.killbill.billing.catalog.api.TimeUnit;
import org.killbill.billing.catalog.api.user.DefaultSimplePlanDescriptor;
import org.killbill.billing.entitlement.api.Entitlement;
import org.killbill.billing.entitlement.api.EntitlementApiException;
+import org.killbill.billing.entitlement.api.Subscription;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
@@ -63,6 +65,7 @@ import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
@@ -274,6 +277,95 @@ public class TestIntegrationWithCatalogUpdate extends TestIntegrationBase {
}
}
+
+ @Test(groups = "slow")
+ public void testWith$0RecurruingPlan() throws Exception {
+
+ // Create a per-tenant catalog with one plan
+ final SimplePlanDescriptor zeroDesc = new DefaultSimplePlanDescriptor("zeroDesc-monthly", "Zero", ProductCategory.BASE, account.getCurrency(), BigDecimal.ZERO, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
+ catalogUserApi.addSimplePlan(zeroDesc, init, testCallContext);
+ StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
+ assertEquals(catalog.getCurrentPlans().size(), 1);
+
+ final PlanPhaseSpecifier specZero = new PlanPhaseSpecifier("zeroDesc-monthly", null);
+
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+ final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), specZero, UUID.randomUUID().toString(), ImmutableList.<PlanPhasePriceOverride>of(), null, null, false, ImmutableList.<PluginProperty>of(), testCallContext);
+ assertListenerStatus();
+
+ Subscription refreshedBaseEntitlement = subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), testCallContext);
+ assertEquals(refreshedBaseEntitlement.getChargedThroughDate(), new LocalDate(2016, 7, 1));
+
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ refreshedBaseEntitlement = subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), testCallContext);
+ assertEquals(refreshedBaseEntitlement.getChargedThroughDate(), new LocalDate(2016, 8, 1));
+
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ refreshedBaseEntitlement = subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), testCallContext);
+ assertEquals(refreshedBaseEntitlement.getChargedThroughDate(), new LocalDate(2016, 9, 1));
+
+
+ // Add another Plan in the catalog
+ final SimplePlanDescriptor descNonZero = new DefaultSimplePlanDescriptor("superfoo-monthly", "SuperFoo", ProductCategory.BASE, account.getCurrency(), new BigDecimal("20.00"), BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
+ catalogUserApi.addSimplePlan(descNonZero, init, testCallContext);
+ catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
+ assertEquals(catalog.getCurrentPlans().size(), 2);
+
+
+ final PlanPhaseSpecifier specNonZero = new PlanPhaseSpecifier("superfoo-monthly", null);
+
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+ final Entitlement baseEntitlement2 = entitlementApi.createBaseEntitlement(account.getId(), specNonZero, UUID.randomUUID().toString(), ImmutableList.<PlanPhasePriceOverride>of(), null, null, false, ImmutableList.<PluginProperty>of(), testCallContext);
+ assertListenerStatus();
+
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ refreshedBaseEntitlement = subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), testCallContext);
+ assertEquals(refreshedBaseEntitlement.getChargedThroughDate(), new LocalDate(2016, 10, 1));
+
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ refreshedBaseEntitlement = subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), testCallContext);
+ assertEquals(refreshedBaseEntitlement.getChargedThroughDate(), new LocalDate(2016, 11, 1));
+
+ busHandler.pushExpectedEvents(NextEvent.BLOCK);
+ baseEntitlement.cancelEntitlementWithDateOverrideBillingPolicy(clock.getUTCToday(), BillingActionPolicy.END_OF_TERM, ImmutableList.<PluginProperty>of(), testCallContext);
+ assertListenerStatus();
+
+
+ busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ refreshedBaseEntitlement = subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), testCallContext);
+ assertEquals(refreshedBaseEntitlement.getChargedThroughDate(), new LocalDate(2016, 11, 1));
+
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ refreshedBaseEntitlement = subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), testCallContext);
+ assertEquals(refreshedBaseEntitlement.getChargedThroughDate(), new LocalDate(2016, 11, 1));
+
+ }
+
+
+
private Entitlement createEntitlement(final String planName, final boolean expectPayment) throws EntitlementApiException {
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(planName, null);
return createEntitlement(spec, null, expectPayment);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java
index 3673a4c..3473252 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java
@@ -45,7 +45,7 @@ public class InvoiceWithMetadata {
this.invoice = originalInvoice;
this.perSubscriptionFutureNotificationDates = perSubscriptionFutureNotificationDates;
build();
- remove$0RecurringAndUsageItems();
+ remove$0UsageItems();
}
public Invoice getInvoice() {
@@ -79,12 +79,12 @@ public class InvoiceWithMetadata {
});
}
- protected void remove$0RecurringAndUsageItems() {
+ protected void remove$0UsageItems() {
if (invoice != null) {
final Iterator<InvoiceItem> it = invoice.getInvoiceItems().iterator();
while (it.hasNext()) {
final InvoiceItem item = it.next();
- if ((item.getInvoiceItemType() == InvoiceItemType.RECURRING || item.getInvoiceItemType() == InvoiceItemType.USAGE) &&
+ if ((item.getInvoiceItemType() == InvoiceItemType.USAGE) &&
item.getAmount().compareTo(BigDecimal.ZERO) == 0) {
it.remove();
}
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 c29c28a..01d03b7 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -484,18 +484,9 @@ public class InvoiceDispatcher {
final Iterable<InvoiceItemModelDao> invoiceItemModelDaos,
final FutureAccountNotifications futureAccountNotifications,
final boolean isRealInvoiceWithItems, final InternalCallContext context) throws SubscriptionBaseApiException, InvoiceApiException {
- // 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).
- final Iterable<InvoiceItemModelDao> filteredInvoiceItemModelDaos = Iterables.filter(invoiceItemModelDaos, new Predicate<InvoiceItemModelDao>() {
- @Override
- public boolean apply(@Nullable final InvoiceItemModelDao input) {
- return (input.getType() != InvoiceItemType.USAGE || input.getAmount().compareTo(BigDecimal.ZERO) != 0);
- }
- });
-
- final boolean isThereAnyItemsLeft = filteredInvoiceItemModelDaos.iterator().hasNext();
+ final boolean isThereAnyItemsLeft = invoiceItemModelDaos.iterator().hasNext();
if (isThereAnyItemsLeft) {
- invoiceDao.createInvoice(invoiceModelDao, ImmutableList.copyOf(filteredInvoiceItemModelDaos), isRealInvoiceWithItems, futureAccountNotifications, context);
+ invoiceDao.createInvoice(invoiceModelDao, ImmutableList.copyOf(invoiceItemModelDaos), isRealInvoiceWithItems, futureAccountNotifications, context);
} else {
invoiceDao.setFutureAccountNotificationsForEmptyInvoice(account.getId(), futureAccountNotifications, context);
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
index 9aeedfe..e104024 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
@@ -629,7 +629,9 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
final Invoice invoice = invoiceWithMetadata.getInvoice();
- assertNull(invoice);
+ assertNotNull(invoice);
+ assertEquals(invoice.getInvoiceItems().size(), 1);
+ assertEquals(invoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.ZERO), 0);
}
@Test(groups = "fast")
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestFixedAndRecurringInvoiceItemGenerator.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestFixedAndRecurringInvoiceItemGenerator.java
index 5fbfc79..5d81097 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestFixedAndRecurringInvoiceItemGenerator.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestFixedAndRecurringInvoiceItemGenerator.java
@@ -78,6 +78,7 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
}
}
+
@Test(groups = "fast")
public void testIsSameDayAndSameSubscriptionWithNullPrevEvent() {
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInvoiceWithMetadata.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInvoiceWithMetadata.java
new file mode 100644
index 0000000..b77cec7
--- /dev/null
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInvoiceWithMetadata.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.invoice.generator;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.catalog.DefaultPrice;
+import org.killbill.billing.catalog.MockInternationalPrice;
+import org.killbill.billing.catalog.MockPlan;
+import org.killbill.billing.catalog.MockPlanPhase;
+import org.killbill.billing.catalog.api.BillingMode;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PhaseType;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanPhase;
+import org.killbill.billing.invoice.InvoiceTestSuiteNoDB;
+import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.generator.InvoiceWithMetadata.SubscriptionFutureNotificationDates;
+import org.killbill.billing.invoice.model.DefaultInvoice;
+import org.killbill.billing.invoice.model.RecurringInvoiceItem;
+import org.killbill.billing.junction.BillingEvent;
+import org.killbill.billing.subscription.api.SubscriptionBase;
+import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.*;
+
+public class TestInvoiceWithMetadata extends InvoiceTestSuiteNoDB {
+
+
+ private Account account;
+ private SubscriptionBase subscription;
+
+ @Override
+ @BeforeMethod(groups = "fast")
+ public void beforeMethod() {
+ super.beforeMethod();
+
+ try {
+ account = invoiceUtil.createAccount(callContext);
+ subscription = invoiceUtil.createSubscription();
+ } catch (final Exception e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @Test(groups = "fast")
+ public void testWith$0RecurringItem() {
+
+
+ final LocalDate invoiceDate = new LocalDate(2016, 11, 15);
+
+ final Invoice originalInvoice = new DefaultInvoice(account.getId(), invoiceDate, account.getCurrency());
+
+ final Plan plan = new MockPlan("my-plan");
+ final MockInternationalPrice price = new MockInternationalPrice(new DefaultPrice(BigDecimal.TEN, account.getCurrency()));
+
+ final PlanPhase planPhase = new MockPlanPhase(price, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
+
+ final BillingEvent event = invoiceUtil.createMockBillingEvent(account,
+ subscription,
+ invoiceDate.toDateTimeAtStartOfDay(),
+ plan,
+ planPhase,
+ null,
+ BigDecimal.ZERO,
+ account.getCurrency(),
+ planPhase.getRecurring().getBillingPeriod(),
+ 1,
+ BillingMode.IN_ADVANCE,
+ "Billing Event Desc",
+ 1L,
+ SubscriptionBaseTransitionType.CREATE);
+
+
+ final InvoiceItem invoiceItem = new RecurringInvoiceItem(UUID.randomUUID(),
+ invoiceDate.toDateTimeAtStartOfDay(),
+ originalInvoice.getId(),
+ account.getId(),
+ subscription.getBundleId(),
+ subscription.getId(),
+ event.getPlan().getName(),
+ event.getPlanPhase().getName(),
+ invoiceDate,
+ invoiceDate.plusMonths(1),
+ BigDecimal.ZERO,
+ BigDecimal.ZERO,
+ account.getCurrency());
+
+ originalInvoice.addInvoiceItem(invoiceItem);
+
+ final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates = new HashMap<UUID, SubscriptionFutureNotificationDates>();
+ final SubscriptionFutureNotificationDates subscriptionFutureNotificationDates = new SubscriptionFutureNotificationDates(BillingMode.IN_ADVANCE);
+ subscriptionFutureNotificationDates.updateNextRecurringDateIfRequired(invoiceDate.plusMonths(1));
+
+ perSubscriptionFutureNotificationDates.put(subscription.getId(), subscriptionFutureNotificationDates);
+
+ final InvoiceWithMetadata invoiceWithMetadata = new InvoiceWithMetadata(originalInvoice, perSubscriptionFutureNotificationDates);
+
+ // We generate an invoice with one item, invoicing for $0
+ final Invoice resultingInvoice = invoiceWithMetadata.getInvoice();
+ Assert.assertNotNull(resultingInvoice);
+ Assert.assertEquals(resultingInvoice.getInvoiceItems().size(), 1);
+ Assert.assertEquals(resultingInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.ZERO), 0);
+
+ final Map<UUID, InvoiceWithMetadata.SubscriptionFutureNotificationDates> dateMap = invoiceWithMetadata.getPerSubscriptionFutureNotificationDates();
+
+ final InvoiceWithMetadata.SubscriptionFutureNotificationDates futureNotificationDates = dateMap.get(subscription.getId());
+
+ // We verify that we generated the future notification for a month ahead
+ Assert.assertNotNull(futureNotificationDates.getNextRecurringDate());
+ Assert.assertEquals(futureNotificationDates.getNextRecurringDate().compareTo(invoiceDate.plusMonths(1)), 0 );
+ }
+
+}
\ No newline at end of file