killbill-aplcache
Changes
beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java 53(+53 -0)
catalog/src/test/resources/catalogTest.xml 48(+48 -0)
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 91ee466..4b040d1 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
@@ -102,6 +102,7 @@ import org.killbill.billing.payment.api.TransactionType;
import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
import org.killbill.billing.subscription.api.SubscriptionBase;
+import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.SubscriptionBaseService;
import org.killbill.billing.subscription.api.timeline.SubscriptionBaseTimelineApi;
import org.killbill.billing.subscription.api.transfer.SubscriptionBaseTransferApi;
@@ -222,6 +223,8 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
@Inject
protected SubscriptionApi subscriptionApi;
+ @Inject
+ protected SubscriptionBaseInternalApi subscriptionBaseInternalApiApi;
@Named(BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME)
@Inject
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
index 607a2b6..d3ffe9c 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
@@ -194,6 +194,59 @@ public class TestConsumableInArrear extends TestIntegrationBase {
assertListenerStatus();
}
+ @Test(groups = "slow")
+ public void testWithNoRecurringPlan() 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);
+
+ // Create subscription
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Trebuchet", ProductCategory.BASE, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
+ // Check bundle after BP got created otherwise we get an error from auditApi.
+ subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
+
+ Assert.assertNull(bpSubscription.getSubscriptionBase().getChargedThroughDate());
+
+ // Record usage for first month
+ setUsage(bpSubscription.getId(), "stones", new LocalDate(2012, 4, 5), 85L, callContext);
+ setUsage(bpSubscription.getId(), "stones", new LocalDate(2012, 4, 15), 150L, callContext);
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.USAGE, new BigDecimal("1000")));
+
+ final DateTime firstExpectedCTD = account.getReferenceTime().withMonthOfYear(5).withDayOfMonth(1);
+ Assert.assertEquals(subscriptionBaseInternalApiApi.getSubscriptionFromId(bpSubscription.getId(), internalCallContext).getChargedThroughDate().compareTo(firstExpectedCTD), 0);
+
+ // No usage in second month
+ busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ Assert.assertEquals(subscriptionBaseInternalApiApi.getSubscriptionFromId(bpSubscription.getId(), internalCallContext).getChargedThroughDate().compareTo(firstExpectedCTD), 0);
+
+ // Record usage for third month (verify invoicing resumes)
+ setUsage(bpSubscription.getId(), "stones", new LocalDate(2012, 6, 5), 25L, callContext);
+ setUsage(bpSubscription.getId(), "stones", new LocalDate(2012, 6, 15), 50L, callContext);
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ invoiceChecker.checkInvoice(account.getId(), 2, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 1), new LocalDate(2012, 7, 1), InvoiceItemType.USAGE, new BigDecimal("100")));
+
+ final DateTime secondExpectedCTD = account.getReferenceTime().withMonthOfYear(7).withDayOfMonth(1);
+ Assert.assertEquals(subscriptionBaseInternalApiApi.getSubscriptionFromId(bpSubscription.getId(), internalCallContext).getChargedThroughDate().compareTo(secondExpectedCTD), 0);
+ }
+
private void setUsage(final UUID subscriptionId, final String unitType, final LocalDate startDate, final Long amount, final CallContext context) throws UsageApiException {
final List<UsageRecord> usageRecords = new ArrayList<UsageRecord>();
usageRecords.add(new UsageRecord(startDate, amount));
catalog/src/test/resources/catalogTest.xml 48(+48 -0)
diff --git a/catalog/src/test/resources/catalogTest.xml b/catalog/src/test/resources/catalogTest.xml
index e4969b1..77a6048 100644
--- a/catalog/src/test/resources/catalogTest.xml
+++ b/catalog/src/test/resources/catalogTest.xml
@@ -41,6 +41,7 @@
<units>
<unit name="bullets"/>
+ <unit name="stones"/>
</units>
<products>
@@ -81,6 +82,9 @@
<addonProduct>Bullets</addonProduct>
</available>
</product>
+ <product name="Trebuchet">
+ <category>BASE</category>
+ </product>
<product name="Cleaning">
<category>ADD_ON</category>
</product>
@@ -1354,6 +1358,49 @@
<plansAllowedInBundle>-1</plansAllowedInBundle>
<!-- arbitrary number of these (multipack) -->
</plan>
+ <plan name="trebuchet-usage-in-arrear" prettyName="Trebuchet Monthly Plan">
+ <product>Trebuchet</product>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <usages>
+ <usage name="trebuchet-in-arrear-usage" billingMode="IN_ARREAR" usageType="CAPACITY">
+ <billingPeriod>MONTHLY</billingPeriod>
+ <tiers>
+ <tier>
+ <limits>
+ <limit>
+ <unit>stones</unit>
+ <max>100</max>
+ </limit>
+ </limits>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>100</value>
+ </price>
+ </recurringPrice>
+ </tier>
+ <tier>
+ <limits>
+ <limit>
+ <unit>stones</unit>
+ <max>-1</max>
+ </limit>
+ </limits>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>1000</value>
+ </price>
+ </recurringPrice>
+ </tier>
+ </tiers>
+ </usage>
+ </usages>
+ </finalPhase>
+ </plan>
</plans>
<priceLists>
<defaultPriceList name="DEFAULT">
@@ -1368,6 +1415,7 @@
<plan>pistol-quarterly</plan>
<plan>shotgun-annual</plan>
<plan>assault-rifle-annual</plan>
+ <plan>trebuchet-usage-in-arrear</plan>
<plan>laser-scope-monthly</plan>
<plan>telescopic-scope-monthly</plan>
<plan>cleaning-monthly</plan>
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 320fcfd..67c1da9 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -831,15 +831,13 @@ public class InvoiceDispatcher {
private void setChargedThroughDates(final Invoice invoice, final InternalCallContext context) throws SubscriptionBaseApiException {
// Don't use invoice.getInvoiceItems(final Class<T> clazz) as some items can come from plugins
- final Collection<InvoiceItem> fixedPriceItems = new LinkedList<InvoiceItem>();
- final Collection<InvoiceItem> recurringItems = new LinkedList<InvoiceItem>();
+ final Collection<InvoiceItem> invoiceItemsToConsider = new LinkedList<InvoiceItem>();
for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) {
switch (invoiceItem.getInvoiceItemType()) {
case FIXED:
- fixedPriceItems.add(invoiceItem);
- break;
case RECURRING:
- recurringItems.add(invoiceItem);
+ case USAGE:
+ invoiceItemsToConsider.add(invoiceItem);
break;
default:
break;
@@ -847,8 +845,7 @@ public class InvoiceDispatcher {
}
final Map<UUID, DateTime> chargeThroughDates = new HashMap<UUID, DateTime>();
- addInvoiceItemsToChargeThroughDates(chargeThroughDates, fixedPriceItems, context);
- addInvoiceItemsToChargeThroughDates(chargeThroughDates, recurringItems, context);
+ addInvoiceItemsToChargeThroughDates(chargeThroughDates, invoiceItemsToConsider, context);
for (final UUID subscriptionId : chargeThroughDates.keySet()) {
if (subscriptionId != null) {