killbill-aplcache

Details

account/pom.xml 2(+1 -1)

diff --git a/account/pom.xml b/account/pom.xml
index b876bcd..a90876a 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-account</artifactId>

api/pom.xml 2(+1 -1)

diff --git a/api/pom.xml b/api/pom.xml
index ef10494..3dc5a1e 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-internal-api</artifactId>

beatrix/pom.xml 2(+1 -1)

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 0a1f312..3b3064f 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
index b79776b..37fa59a 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
@@ -663,4 +663,73 @@ public class TestIntegration extends TestIntegrationBase {
 
         checkNoMoreInvoiceToGenerate(account);
     }
+
+    @Test(groups = "slow")
+    public void testFixedTermPlan() 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(2015, 4, 1));
+
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+
+        // First invoice
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, /* NextEvent.BLOCK, */ NextEvent.INVOICE);
+
+
+        // Second invoice -> first recurring for Refurbish-Maintenance
+        addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Refurbish-Maintenance", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, /* NextEvent.BLOCK, */ NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("599.95")));
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 4, 1), new LocalDate(2015, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("199.95")));
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 5, 1), new LocalDate(2015, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("199.95")));
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 5, 1), new LocalDate(2015, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+
+        // 2015-5-1
+        // Third invoice -> second recurring for Refurbish-Maintenance
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(30); // Also = 1 month because or initial date 2015, 4, 1
+        assertListenerStatus();
+
+        invoiceChecker.checkInvoice(account.getId(), 3, callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+
+        // Next 10 invoices -> third to twelve **and last** recurring for Refurbish-Maintenance
+        LocalDate startDate = new LocalDate(2015, 6, 1);
+        for (int i = 0; i < 10; i++) {
+
+            final LocalDate endDate = startDate.plusMonths(1);
+            expectedInvoices.add(new ExpectedInvoiceItemCheck(startDate, endDate, InvoiceItemType.RECURRING, new BigDecimal("199.95")));
+            expectedInvoices.add(new ExpectedInvoiceItemCheck(startDate, endDate, InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+
+            busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+            clock.addMonths(1);
+            assertListenerStatus();
+
+            invoiceChecker.checkInvoice(account.getId(), 4 + i, callContext, expectedInvoices);
+            expectedInvoices.clear();
+
+            startDate = endDate;
+        }
+
+        // We check there is no more recurring for Refurbish-Maintenance
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 4, 1), new LocalDate(2016, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+
+        invoiceChecker.checkInvoice(account.getId(), 14, callContext, expectedInvoices);
+        expectedInvoices.clear();
+    }
 }

catalog/pom.xml 2(+1 -1)

diff --git a/catalog/pom.xml b/catalog/pom.xml
index e75310f..92e9692 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-catalog</artifactId>
diff --git a/catalog/src/test/resources/catalogTest.xml b/catalog/src/test/resources/catalogTest.xml
index 56576c4..4b1ee25 100644
--- a/catalog/src/test/resources/catalogTest.xml
+++ b/catalog/src/test/resources/catalogTest.xml
@@ -50,6 +50,7 @@
             <available>
                 <addonProduct>Cleaning</addonProduct>
                 <addonProduct>Bullets</addonProduct>
+                <addonProduct>Refurbish-Maintenance</addonProduct>
             </available>
         </product>
         <product name="Shotgun">

currency/pom.xml 2(+1 -1)

diff --git a/currency/pom.xml b/currency/pom.xml
index 049a773..748b65e 100644
--- a/currency/pom.xml
+++ b/currency/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-currency</artifactId>
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 83f81aa..0537b95 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>

invoice/pom.xml 2(+1 -1)

diff --git a/invoice/pom.xml b/invoice/pom.xml
index 4614397..cd544ac 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
index e48bd18..1ac8d6a 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
@@ -33,9 +33,12 @@ import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.api.BillingMode;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.Duration;
+import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.generator.InvoiceWithMetadata.SubscriptionFutureNotificationDates;
 import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
 import org.killbill.billing.invoice.model.InvalidDateSequenceException;
@@ -50,6 +53,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 
 import static org.killbill.billing.invoice.generator.InvoiceDateUtils.calculateNumberOfWholeBillingPeriods;
@@ -83,9 +88,8 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
 
         // Generate list of proposed invoice items based on billing events from junction-- proposed items are ALL items since beginning of time
         final List<InvoiceItem> proposedItems = new ArrayList<InvoiceItem>();
-        processRecurringBillingEvents(invoiceId, account.getId(), eventSet, targetDate, targetCurrency, proposedItems, perSubscriptionFutureNotificationDate, internalCallContext);
+        processRecurringBillingEvents(invoiceId, account.getId(), eventSet, targetDate, targetCurrency, proposedItems, perSubscriptionFutureNotificationDate, existingInvoices, internalCallContext);
         processFixedBillingEvents(invoiceId, account.getId(), eventSet, targetDate, targetCurrency, proposedItems, internalCallContext);
-
         accountItemTree.mergeWithProposedItems(proposedItems);
         return accountItemTree.getResultingItemList();
     }
@@ -93,6 +97,7 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
     private void processRecurringBillingEvents(final UUID invoiceId, final UUID accountId, final BillingEventSet events,
                                                final LocalDate targetDate, final Currency currency, final List<InvoiceItem> proposedItems,
                                                final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
+                                               @Nullable final List<Invoice> existingInvoices,
                                                final InternalCallContext internalCallContext) throws InvoiceApiException {
         if (events.size() == 0) {
             return;
@@ -112,11 +117,12 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
             if (!events.getSubscriptionIdsWithAutoInvoiceOff().
                     contains(thisEvent.getSubscription().getId())) { // don't consider events for subscriptions that have auto_invoice_off
                 final BillingEvent adjustedNextEvent = (thisEvent.getSubscription().getId() == nextEvent.getSubscription().getId()) ? nextEvent : null;
-                final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, thisEvent, adjustedNextEvent, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext);
+                final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, thisEvent, adjustedNextEvent, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, existingInvoices, internalCallContext);
                 proposedItems.addAll(newProposedItems);
             }
         }
-        final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, nextEvent, null, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext);
+        final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, nextEvent, null, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, existingInvoices, internalCallContext);
+
         proposedItems.addAll(newProposedItems);
 
         log.info(logStringBuilder.toString());
@@ -162,9 +168,15 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
                                                     final LocalDate targetDate, final Currency currency,
                                                     final StringBuilder logStringBuilder, final BillingMode billingMode,
                                                     final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
+                                                    @Nullable final List<Invoice> existingInvoices,
                                                     final InternalCallContext internalCallContext) throws InvoiceApiException {
         final List<InvoiceItem> items = new ArrayList<InvoiceItem>();
 
+        // For FIXEDTERM phases we need to stop when the specified duration has been reached
+        final LocalDate maxEndDate = thisEvent.getPlanPhase().getPhaseType() == PhaseType.FIXEDTERM ?
+                                     computeMaxEndDateForFixedTermPlanPhase(thisEvent, existingInvoices) :
+                                     null;
+
         // Handle recurring items
         final BillingPeriod billingPeriod = thisEvent.getBillingPeriod();
         if (billingPeriod != BillingPeriod.NO_BILLING_PERIOD) {
@@ -183,8 +195,12 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
                 }
 
                 for (final RecurringInvoiceItemData itemDatum : itemDataWithNextBillingCycleDate.getItemData()) {
-                    final BigDecimal rate = thisEvent.getRecurringPrice();
 
+                    // Stop if there a maxEndDate and we have reached it
+                    if (maxEndDate != null && maxEndDate.compareTo(itemDatum.getEndDate()) < 0) {
+                        break;
+                    }
+                    final BigDecimal rate = thisEvent.getRecurringPrice();
                     if (rate != null) {
                         final BigDecimal amount = KillBillMoney.of(itemDatum.getNumberOfCycles().multiply(rate), currency);
                         final RecurringInvoiceItem recurringItem = new RecurringInvoiceItem(invoiceId,
@@ -193,7 +209,8 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
                                                                                             thisEvent.getSubscription().getId(),
                                                                                             thisEvent.getPlan().getName(),
                                                                                             thisEvent.getPlanPhase().getName(),
-                                                                                            itemDatum.getStartDate(), itemDatum.getEndDate(),
+                                                                                            itemDatum.getStartDate(),
+                                                                                            itemDatum.getEndDate(),
                                                                                             amount, rate, currency);
                         items.add(recurringItem);
                     }
@@ -212,6 +229,38 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
         return items;
     }
 
+    //
+    // Go through invoices in reverse order and for each invoice extract a possible item for that subscription and the phaseName associated with this billing event
+    // Computes the startDate for that first one seen in that uninterrupted sequence and then add the FIXEDTERM duration to compute the max endDate
+    //
+    private LocalDate computeMaxEndDateForFixedTermPlanPhase(final BillingEvent thisEvent, @Nullable final List<Invoice> existingInvoices) {
+        if (existingInvoices == null || existingInvoices.isEmpty()) {
+            return null;
+        }
+
+        LocalDate firstStartDate  = null;
+        for (int i = existingInvoices.size() - 1; i >= 0; i--) {
+            final Invoice cur = existingInvoices.get(i);
+
+            final InvoiceItem matchItem = Iterables.tryFind(cur.getInvoiceItems(), new Predicate<InvoiceItem>() {
+                @Override
+                public boolean apply(final InvoiceItem input) {
+                    return input.getInvoiceItemType() == InvoiceItemType.RECURRING &&
+                           thisEvent.getSubscription().getId().equals(input.getSubscriptionId()) &&
+                           thisEvent.getPlanPhase().getName().equals(input.getPhaseName());
+                }
+            }).orNull();
+
+            if (matchItem == null) {
+                break;
+            }
+            firstStartDate = matchItem.getStartDate();
+        }
+
+        final LocalDate result  = addDurationToLocalDate(firstStartDate, thisEvent.getPlanPhase().getDuration());
+        return result;
+    }
+
     private void updatePerSubscriptionNextNotificationDate(final UUID subscriptionId, final LocalDate nextBillingCycleDate, final List<InvoiceItem> newProposedItems, final BillingMode billingMode, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates) {
 
         LocalDate nextNotificationDate = null;
@@ -355,4 +404,26 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
             }
         }
     }
+
+    // That code should belong to Duration/DefaultDuration but requires a change api (not possible for 0.16.3, but will be moreved in 0.17.0)
+    private LocalDate addDurationToLocalDate(@Nullable final LocalDate inputDate, final Duration duration) {
+
+        if (inputDate == null) {
+            return inputDate;
+        }
+
+        switch (duration.getUnit()) {
+            case DAYS:
+                return inputDate.plusDays(duration.getNumber());
+            case MONTHS:
+                return inputDate.plusMonths(duration.getNumber());
+            case YEARS:
+                return inputDate.plusYears(duration.getNumber());
+            case UNLIMITED:
+                return inputDate.plusYears(100);
+            default:
+                throw new IllegalStateException("Unknwon duration " + duration.getUnit());
+        }
+    }
+
 }

jaxrs/pom.xml 2(+1 -1)

diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index d0f56d5..1b7d890 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-jaxrs</artifactId>
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
index c11ff57..b55db89 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
@@ -130,7 +130,6 @@ public class UsageResource extends JaxRsResourceBase {
                              @QueryParam(QUERY_START_DATE) final String startDate,
                              @QueryParam(QUERY_END_DATE) final String endDate,
                              @javax.ws.rs.core.Context final HttpServletRequest request) {
-
         if (startDate == null || endDate == null) {
             return Response.status(Status.BAD_REQUEST).build();
         }

junction/pom.xml 2(+1 -1)

diff --git a/junction/pom.xml b/junction/pom.xml
index 422ded4..16f2cd0 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-junction</artifactId>

NEWS 3(+3 -0)

diff --git a/NEWS b/NEWS
index 8001aa9..3201911 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,6 @@
+0.16.2
+    See https://github.com/killbill/killbill/releases/tag/killbill-0.16.2
+
 0.16.1
     See https://github.com/killbill/killbill/releases/tag/killbill-0.16.1
 

overdue/pom.xml 2(+1 -1)

diff --git a/overdue/pom.xml b/overdue/pom.xml
index e7f373d..47b77b1 100644
--- a/overdue/pom.xml
+++ b/overdue/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-overdue</artifactId>

payment/pom.xml 2(+1 -1)

diff --git a/payment/pom.xml b/payment/pom.xml
index de0ab68..f33921d 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java
index 4ead401..9a87984 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlInitiated.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * 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
@@ -63,6 +63,7 @@ public class DefaultControlInitiated implements LeavingStateCallback {
             final PaymentModelDao payment = paymentDao.getPayment(stateContext.getPaymentId(), stateContext.getInternalCallContext());
             Preconditions.checkNotNull(payment, "payment cannot be null for id " + stateContext.getPaymentId());
             stateContext.setPaymentExternalKey(payment.getExternalKey());
+            stateContext.setPaymentMethodId(payment.getPaymentMethodId());
         } else if (stateContext.getPaymentExternalKey() == null) {
             stateContext.setPaymentExternalKey(UUIDs.randomUUID().toString());
         }
@@ -74,6 +75,11 @@ public class DefaultControlInitiated implements LeavingStateCallback {
             stateContext.setPaymentTransactionExternalKey(UUIDs.randomUUID().toString());
         }
 
+        if (stateContext.getPaymentMethodId() == null) {
+            // Similar logic in PaymentAutomatonRunner
+            stateContext.setPaymentMethodId(stateContext.getAccount().getPaymentMethodId());
+        }
+
         if (state.getName().equals(initialState.getName()) || state.getName().equals(retriedState.getName())) {
             try {
                 //
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java
index 8b51735..2841907 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PaymentStateControlContext.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * 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
@@ -72,7 +72,6 @@ public class PaymentStateControlContext extends PaymentStateContext {
         this.result = result;
     }
 
-
     public PaymentTransaction getCurrentTransaction() {
         if (result == null || result.getTransactions() == null) {
             return null;
@@ -80,7 +79,8 @@ public class PaymentStateControlContext extends PaymentStateContext {
         return Iterables.tryFind(result.getTransactions(), new Predicate<PaymentTransaction>() {
             @Override
             public boolean apply(final PaymentTransaction input) {
-                return ((DefaultPaymentTransaction) input).getAttemptId().equals(getAttemptId());
+                final DefaultPaymentTransaction defaultPaymentTransaction = (DefaultPaymentTransaction) input;
+                return defaultPaymentTransaction.getAttemptId() == null ? getAttemptId() == null : defaultPaymentTransaction.getAttemptId().equals(getAttemptId());
             }
         }).orNull();
     }
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiWithControl.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiWithControl.java
index aaffeda..acb1be0 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiWithControl.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiWithControl.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * 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
@@ -23,17 +23,17 @@ import java.util.UUID;
 
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
-import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
-import org.killbill.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
-import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
 import org.killbill.billing.control.plugin.api.OnFailurePaymentControlResult;
 import org.killbill.billing.control.plugin.api.OnSuccessPaymentControlResult;
 import org.killbill.billing.control.plugin.api.PaymentControlApiException;
 import org.killbill.billing.control.plugin.api.PaymentControlContext;
 import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
 import org.killbill.billing.control.plugin.api.PriorPaymentControlResult;
+import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
+import org.killbill.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
+import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
 import org.killbill.billing.payment.retry.DefaultFailureCallResult;
 import org.killbill.billing.payment.retry.DefaultOnSuccessPaymentControlResult;
 import org.testng.Assert;
@@ -45,45 +45,107 @@ import com.google.inject.Inject;
 
 public class TestPaymentApiWithControl extends PaymentTestSuiteWithEmbeddedDB {
 
+    private static final PaymentOptions PAYMENT_OPTIONS = new PaymentOptions() {
+        @Override
+        public boolean isExternalPayment() {
+            return false;
+        }
+
+        @Override
+        public List<String> getPaymentControlPluginNames() {
+            return ImmutableList.of(TestPaymentControlPluginApi.PLUGIN_NAME);
+        }
+    };
+
     @Inject
     private OSGIServiceRegistration<PaymentControlPluginApi> controlPluginRegistry;
 
     private Account account;
-    private UUID newPaymentMethodId;
+    private TestPaymentControlPluginApi testPaymentControlPluginApi;
 
     @BeforeMethod(groups = "slow")
     public void beforeMethod() throws Exception {
         super.beforeMethod();
         account = testHelper.createTestAccount("bobo@gmail.com", true);
-        final PaymentMethodPlugin paymentMethodInfo = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), false, null);
-        newPaymentMethodId = paymentApi.addPaymentMethod(account, paymentMethodInfo.getExternalPaymentMethodId(), MockPaymentProviderPlugin.PLUGIN_NAME, false, paymentMethodInfo, ImmutableList.<PluginProperty>of(), callContext);
 
+        testPaymentControlPluginApi = new TestPaymentControlPluginApi();
         controlPluginRegistry.registerService(new OSGIServiceDescriptor() {
-            @Override
-            public String getPluginSymbolicName() {
-                return null;
-            }
-
-            @Override
-            public String getPluginName() {
-                return TestPaymentControlPluginApi.PLUGIN_NAME;
-            }
-
-            @Override
-            public String getRegistrationName() {
-                return TestPaymentControlPluginApi.PLUGIN_NAME;
-            }
-        }, new TestPaymentControlPluginApi(newPaymentMethodId));
+                                                  @Override
+                                                  public String getPluginSymbolicName() {
+                                                      return null;
+                                                  }
+
+                                                  @Override
+                                                  public String getPluginName() {
+                                                      return TestPaymentControlPluginApi.PLUGIN_NAME;
+                                                  }
+
+                                                  @Override
+                                                  public String getRegistrationName() {
+                                                      return TestPaymentControlPluginApi.PLUGIN_NAME;
+                                                  }
+                                              },
+                                              testPaymentControlPluginApi);
+    }
 
+    // Verify Payment control API can be used to change the paymentMethodId on the fly and this is reflected in the created Payment.
+    @Test(groups = "slow")
+    public void testCreateAuthWithControl() throws PaymentApiException {
+        final PaymentMethodPlugin paymentMethodInfo = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), false, null);
+        final UUID newPaymentMethodId = paymentApi.addPaymentMethod(account, paymentMethodInfo.getExternalPaymentMethodId(), MockPaymentProviderPlugin.PLUGIN_NAME, false, paymentMethodInfo, ImmutableList.<PluginProperty>of(), callContext);
+        testPaymentControlPluginApi.setNewPaymentMethodId(newPaymentMethodId);
+
+        final Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, account.getPaymentMethodId(), null, BigDecimal.TEN, Currency.USD, UUID.randomUUID().toString(),
+                                                                                 UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), PAYMENT_OPTIONS, callContext);
+        Assert.assertEquals(payment.getPaymentMethodId(), newPaymentMethodId);
+    }
+
+    @Test(groups = "slow")
+    public void testCreateAuthWithControlCaptureNoControl() throws PaymentApiException {
+        final BigDecimal requestedAmount = BigDecimal.TEN;
+
+        Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, UUID.randomUUID().toString(),
+                                                                           UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), PAYMENT_OPTIONS, callContext);
+        Assert.assertEquals(payment.getAuthAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+        Assert.assertEquals(payment.getTransactions().size(), 1);
+        Assert.assertNotNull(((DefaultPaymentTransaction) payment.getTransactions().get(0)).getAttemptId());
+
+        payment = paymentApi.createCapture(account, payment.getId(), payment.getAuthAmount(), payment.getCurrency(), UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertEquals(payment.getAuthAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(payment.getCapturedAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(payment.getTransactions().size(), 2);
+        Assert.assertNotNull(((DefaultPaymentTransaction) payment.getTransactions().get(0)).getAttemptId());
+        Assert.assertNull(((DefaultPaymentTransaction) payment.getTransactions().get(1)).getAttemptId());
+    }
+
+    @Test(groups = "slow")
+    public void testCreateAuthNoControlCaptureWithControl() throws PaymentApiException {
+        final BigDecimal requestedAmount = BigDecimal.TEN;
+
+        Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, UUID.randomUUID().toString(),
+                                                         UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertEquals(payment.getAuthAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+        Assert.assertEquals(payment.getTransactions().size(), 1);
+        Assert.assertNull(((DefaultPaymentTransaction) payment.getTransactions().get(0)).getAttemptId());
+
+        payment = paymentApi.createCaptureWithPaymentControl(account, payment.getId(), payment.getAuthAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
+                                                             ImmutableList.<PluginProperty>of(), PAYMENT_OPTIONS, callContext);
+        Assert.assertEquals(payment.getAuthAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(payment.getCapturedAmount().compareTo(requestedAmount), 0);
+        Assert.assertEquals(payment.getTransactions().size(), 2);
+        Assert.assertNull(((DefaultPaymentTransaction) payment.getTransactions().get(0)).getAttemptId());
+        Assert.assertNotNull(((DefaultPaymentTransaction) payment.getTransactions().get(1)).getAttemptId());
     }
 
     public static class TestPaymentControlPluginApi implements PaymentControlPluginApi {
 
         public static final String PLUGIN_NAME = "TEST_CONTROL_API_PLUGIN_NAME";
 
-        private final UUID newPaymentMethodId;
+        private UUID newPaymentMethodId;
 
-        public TestPaymentControlPluginApi(final UUID newPaymentMethodId) {
+        public void setNewPaymentMethodId(final UUID newPaymentMethodId) {
             this.newPaymentMethodId = newPaymentMethodId;
         }
 
@@ -127,31 +189,4 @@ public class TestPaymentApiWithControl extends PaymentTestSuiteWithEmbeddedDB {
             return new DefaultFailureCallResult(null);
         }
     }
-
-    // Verify Payment control API can be used to change the paymentMethodId on the fly and this is reflected in the created Payment.
-    @Test(groups = "slow")
-    public void testCreateAuthWithControl() throws PaymentApiException {
-
-        final BigDecimal requestedAmount = BigDecimal.TEN;
-
-        final String paymentExternalKey = "dfdf";
-        final String transactionExternalKey = "qwqwqw";
-
-        final PaymentOptions paymentOptions = new PaymentOptions() {
-            @Override
-            public boolean isExternalPayment() {
-                return false;
-            }
-
-            @Override
-            public List<String> getPaymentControlPluginNames() {
-                return ImmutableList.of(TestPaymentControlPluginApi.PLUGIN_NAME);
-            }
-        };
-
-        final Payment payment = paymentApi.createAuthorizationWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey,
-                                                                                 ImmutableList.<PluginProperty>of(), paymentOptions, callContext);
-
-        Assert.assertEquals(payment.getPaymentMethodId(), newPaymentMethodId);
-    }
 }

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index e9f27a9..8fb6873 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
         <version>0.83-SNAPSHOT</version>
     </parent>
     <artifactId>killbill</artifactId>
-    <version>0.16.2-SNAPSHOT</version>
+    <version>0.16.3-SNAPSHOT</version>
     <packaging>pom</packaging>
     <name>killbill</name>
     <description>Library for managing recurring subscriptions and the associated billing</description>
diff --git a/profiles/killbill/pom.xml b/profiles/killbill/pom.xml
index 20c2b32..a698349 100644
--- a/profiles/killbill/pom.xml
+++ b/profiles/killbill/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill-profiles</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles-killbill</artifactId>
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
index 752ef48..e310cc1 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * 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
@@ -93,6 +93,105 @@ public class TestInvoice extends TestJaxrsBase {
         final Invoice firstInvoiceByNumberJson = killBillClient.getInvoice(invoiceJson.getInvoiceNumber());
         assertEquals(firstInvoiceByNumberJson, invoiceJson);
 
+        // Check we can retrieve the HTML version
+        final String htmlInvoice = killBillClient.getInvoiceAsHtml(invoiceJson.getInvoiceId());
+        assertEquals(htmlInvoice, "<html>\n" +
+                                  "    <head>\n" +
+                                  "        <style type=\"text/css\">\n" +
+                                  "            th {align=left; width=225px; border-bottom: solid 2px black;}\n" +
+                                  "        </style>\n" +
+                                  "    </head>\n" +
+                                  "    <body>\n" +
+                                  "        <h1>invoiceTitle</h1>\n" +
+                                  "        <table>\n" +
+                                  "            <tr>\n" +
+                                  "                <td rowspan=3 width=350px>Insert image here</td>\n" +
+                                  "                <td width=100px/>\n" +
+                                  "                <td width=225px/>\n" +
+                                  "                <td width=225px/>\n" +
+                                  "            </tr>\n" +
+                                  "            <tr>\n" +
+                                  "                <td />\n" +
+                                  "                <td align=right>invoiceDate</td>\n" +
+                                  "                <td>25 avr. 2012</td>\n" +
+                                  "            </tr>\n" +
+                                  "            <tr>\n" +
+                                  "                <td />\n" +
+                                  "                <td align=right>invoiceNumber</td>\n" +
+                                  "                <td>1</td>\n" +
+                                  "            </tr>\n" +
+                                  "            <tr>\n" +
+                                  "                <td>companyName</td>\n" +
+                                  "                <td></td>\n" +
+                                  "                <td align=right>accountOwnerName</td>\n" +
+                                  "                <td>" + accountJson.getName() + "</td>\n" +
+                                  "            </tr>\n" +
+                                  "            <tr>\n" +
+                                  "                <td>companyAddress</td>\n" +
+                                  "                <td />\n" +
+                                  "                <td />\n" +
+                                  "                <td>" + accountJson.getEmail() + "</td>\n" +
+                                  "            </tr>\n" +
+                                  "            <tr>\n" +
+                                  "                <td>companyCityProvincePostalCode</td>\n" +
+                                  "                <td />\n" +
+                                  "                <td />\n" +
+                                  "                <td>" + accountJson.getPhone() + "</td>\n" +
+                                  "            </tr>\n" +
+                                  "            <tr>\n" +
+                                  "                <td>companyCountry</td>\n" +
+                                  "                <td />\n" +
+                                  "                <td />\n" +
+                                  "                <td />\n" +
+                                  "            </tr>\n" +
+                                  "            <tr>\n" +
+                                  "                <td><companyUrl</td>\n" +
+                                  "                <td />\n" +
+                                  "                <td />\n" +
+                                  "                <td />\n" +
+                                  "            </tr>\n" +
+                                  "        </table>\n" +
+                                  "        <br />\n" +
+                                  "        <br />\n" +
+                                  "        <br />\n" +
+                                  "        <table>\n" +
+                                  "            <tr>\n" +
+                                  "                <th>invoiceItemBundleName</td>\n" +
+                                  "                <th>invoiceItemDescription</td>\n" +
+                                  "                <th>invoiceItemServicePeriod</td>\n" +
+                                  "                <th>invoiceItemAmount</td>\n" +
+                                  "            </tr>\n" +
+                                  "            \n" +
+                                  "            <tr>\n" +
+                                  "                <td>shotgun-monthly-trial</td>\n" +
+                                  "                <td>Monthly shotgun plan</td>\n" +
+                                  "                <td>25 avr. 2012</td>\n" +
+                                  "                <td>USD 0E-9</td>\n" +
+                                  "            </tr>\n" +
+                                  "            \n" +
+                                  "            <tr>\n" +
+                                  "                <td colspan=4 />\n" +
+                                  "            </tr>\n" +
+                                  "            <tr>\n" +
+                                  "                <td colspan=2 />\n" +
+                                  "                <td align=right><strong>invoiceAmount</strong></td>\n" +
+                                  "                <td align=right><strong>0.00</strong></td>\n" +
+                                  "            </tr>\n" +
+                                  "            <tr>\n" +
+                                  "                <td colspan=2 />\n" +
+                                  "                <td align=right><strong>invoiceAmountPaid</strong></td>\n" +
+                                  "                <td align=right><strong>0</strong></td>\n" +
+                                  "            </tr>\n" +
+                                  "            <tr>\n" +
+                                  "                <td colspan=2 />\n" +
+                                  "                <td align=right><strong>invoiceBalance</strong></td>\n" +
+                                  "                <td align=right><strong>0.00</strong></td>\n" +
+                                  "            </tr>\n" +
+                                  "        </table>\n" +
+                                  "    </body>\n" +
+                                  "</html>\n" +
+                                  "\n");
+
         // Then create a dryRun for next upcoming invoice
         final InvoiceDryRun dryRunArg = new InvoiceDryRun(DryRunType.UPCOMING_INVOICE, null,
                                                           null, null, null, null, null, null, null, null, null, null);
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestUsage.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestUsage.java
new file mode 100644
index 0000000..2e9dd6a
--- /dev/null
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestUsage.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 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.jaxrs;
+
+import java.util.UUID;
+
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.client.model.Account;
+import org.killbill.billing.client.model.Bundle;
+import org.killbill.billing.client.model.RolledUpUsage;
+import org.killbill.billing.client.model.Subscription;
+import org.killbill.billing.client.model.SubscriptionUsageRecord;
+import org.killbill.billing.client.model.UnitUsageRecord;
+import org.killbill.billing.client.model.UsageRecord;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class TestUsage extends TestJaxrsBase {
+
+    @Test(groups = "slow", description = "Can record and retrieve usage data")
+    public void testRecordUsage() throws Exception {
+        final Account accountJson = createAccountWithDefaultPaymentMethod();
+
+        final Subscription base = new Subscription();
+        base.setAccountId(accountJson.getAccountId());
+        base.setProductName("Pistol");
+        base.setProductCategory(ProductCategory.BASE);
+        base.setBillingPeriod(BillingPeriod.MONTHLY);
+        base.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+        final Subscription addOn = new Subscription();
+        addOn.setAccountId(accountJson.getAccountId());
+        addOn.setProductName("Bullets");
+        addOn.setProductCategory(ProductCategory.ADD_ON);
+        addOn.setBillingPeriod(BillingPeriod.NO_BILLING_PERIOD);
+        addOn.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+        final Bundle bundle = killBillClient.createSubscriptionWithAddOns(ImmutableList.<Subscription>of(base, addOn),
+                                                                          null,
+                                                                          DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC,
+                                                                          createdBy,
+                                                                          reason,
+                                                                          comment);
+        final UUID addOnSubscriptionId = Iterables.<Subscription>find(bundle.getSubscriptions(),
+                                                                      new Predicate<Subscription>() {
+                                                                          @Override
+                                                                          public boolean apply(final Subscription input) {
+                                                                              return ProductCategory.ADD_ON.equals(input.getProductCategory());
+                                                                          }
+                                                                      }).getSubscriptionId();
+
+        clock.addDays(1);
+
+        final UsageRecord usageRecord1 = new UsageRecord();
+        usageRecord1.setAmount(10L);
+        usageRecord1.setRecordDate(clock.getUTCToday().minusDays(1));
+
+        final UsageRecord usageRecord2 = new UsageRecord();
+        usageRecord2.setAmount(5L);
+        usageRecord2.setRecordDate(clock.getUTCToday());
+
+        final UnitUsageRecord unitUsageRecord = new UnitUsageRecord();
+        unitUsageRecord.setUnitType("bullets");
+        unitUsageRecord.setUsageRecords(ImmutableList.<UsageRecord>of(usageRecord1, usageRecord2));
+
+        final SubscriptionUsageRecord usage = new SubscriptionUsageRecord();
+        usage.setSubscriptionId(addOnSubscriptionId);
+        usage.setUnitUsageRecords(ImmutableList.<UnitUsageRecord>of(unitUsageRecord));
+
+        killBillClient.createSubscriptionUsageRecord(usage, createdBy, reason, comment);
+
+        final RolledUpUsage retrievedUsage1 = killBillClient.getRolledUpUsage(addOnSubscriptionId, unitUsageRecord.getUnitType(), clock.getUTCToday().minusDays(1), clock.getUTCToday());
+        Assert.assertEquals(retrievedUsage1.getSubscriptionId(), usage.getSubscriptionId());
+        Assert.assertEquals(retrievedUsage1.getRolledUpUnits().size(), 1);
+        Assert.assertEquals(retrievedUsage1.getRolledUpUnits().get(0).getUnitType(), unitUsageRecord.getUnitType());
+        // endDate is excluded
+        Assert.assertEquals((long) retrievedUsage1.getRolledUpUnits().get(0).getAmount(), 10);
+
+        final RolledUpUsage retrievedUsage2 = killBillClient.getRolledUpUsage(addOnSubscriptionId, unitUsageRecord.getUnitType(), clock.getUTCToday().minusDays(1), clock.getUTCToday().plusDays(1));
+        Assert.assertEquals(retrievedUsage2.getSubscriptionId(), usage.getSubscriptionId());
+        Assert.assertEquals(retrievedUsage2.getRolledUpUnits().size(), 1);
+        Assert.assertEquals(retrievedUsage2.getRolledUpUnits().get(0).getUnitType(), unitUsageRecord.getUnitType());
+        Assert.assertEquals((long) retrievedUsage2.getRolledUpUnits().get(0).getAmount(), 15);
+
+        final RolledUpUsage retrievedUsage3 = killBillClient.getRolledUpUsage(addOnSubscriptionId, unitUsageRecord.getUnitType(), clock.getUTCToday(), clock.getUTCToday().plusDays(1));
+        Assert.assertEquals(retrievedUsage3.getSubscriptionId(), usage.getSubscriptionId());
+        Assert.assertEquals(retrievedUsage3.getRolledUpUnits().size(), 1);
+        Assert.assertEquals(retrievedUsage3.getRolledUpUnits().get(0).getUnitType(), unitUsageRecord.getUnitType());
+        Assert.assertEquals((long) retrievedUsage3.getRolledUpUnits().get(0).getAmount(), 5);
+
+        final RolledUpUsage retrievedUsage4 = killBillClient.getRolledUpUsage(addOnSubscriptionId, null, clock.getUTCToday(), clock.getUTCToday().plusDays(1));
+        Assert.assertEquals(retrievedUsage4.getSubscriptionId(), usage.getSubscriptionId());
+        Assert.assertEquals(retrievedUsage4.getRolledUpUnits().size(), 1);
+        Assert.assertEquals(retrievedUsage4.getRolledUpUnits().get(0).getUnitType(), "bullets");
+        Assert.assertEquals((long) retrievedUsage4.getRolledUpUnits().get(0).getAmount(), 5);
+    }
+}
diff --git a/profiles/killpay/pom.xml b/profiles/killpay/pom.xml
index 6d43052..829b07a 100644
--- a/profiles/killpay/pom.xml
+++ b/profiles/killpay/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>killbill-profiles</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles-killpay</artifactId>

profiles/pom.xml 2(+1 -1)

diff --git a/profiles/pom.xml b/profiles/pom.xml
index 442ac18..9bf3c9f 100644
--- a/profiles/pom.xml
+++ b/profiles/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles</artifactId>
diff --git a/subscription/pom.xml b/subscription/pom.xml
index 825e97b..5ae804d 100644
--- a/subscription/pom.xml
+++ b/subscription/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-subscription</artifactId>

tenant/pom.xml 2(+1 -1)

diff --git a/tenant/pom.xml b/tenant/pom.xml
index a106e2b..9662fc8 100644
--- a/tenant/pom.xml
+++ b/tenant/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-tenant</artifactId>

usage/pom.xml 2(+1 -1)

diff --git a/usage/pom.xml b/usage/pom.xml
index 4d9a683..d316e77 100644
--- a/usage/pom.xml
+++ b/usage/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-usage</artifactId>

util/pom.xml 2(+1 -1)

diff --git a/util/pom.xml b/util/pom.xml
index 508a73f..0e250e7 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.2-SNAPSHOT</version>
+        <version>0.16.3-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>