killbill-memoizeit

Changes

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

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

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

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

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

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

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

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

NEWS 5(+5 -0)

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

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

pom.xml 4(+2 -2)

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

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

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

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

Details

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

diff --git a/account/pom.xml b/account/pom.xml
index 1dc6009..f60a1e3 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-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 fe60cf3..eb96d0d 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-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 95a0665..c6f8d74 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>

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

diff --git a/catalog/pom.xml b/catalog/pom.xml
index 6f4c027..71127c8 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-catalog</artifactId>
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
index db734df..f9c1c0d 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
  *
@@ -174,9 +176,6 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         return this.priceLists;
     }
 
-    /* (non-Javadoc)
-      * @see org.killbill.billing.catalog.ICatalog#getPlan(java.lang.String, java.lang.String)
-      */
     @Override
     public Plan createOrFindCurrentPlan(final PlanSpecifier spec, final PlanPhasePriceOverridesWithCallContext unused) throws CatalogApiException {
         final Plan result;
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
index 32018fd..f7d8036 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
@@ -157,20 +157,22 @@ public class VersionedCatalog extends ValidatingConfig<VersionedCatalog> impleme
 
     private CatalogPlanEntry findCatalogPlanEntry(final PlanRequestWrapper wrapper,
                                                   final DateTime requestedDate,
-                                                  final DateTime subscriptionStartDate)
-            throws CatalogApiException {
+                                                  final DateTime subscriptionStartDate) throws CatalogApiException {
         final List<StandaloneCatalog> catalogs = versionsBeforeDate(requestedDate.toDate());
         if (catalogs.isEmpty()) {
             throw new CatalogApiException(ErrorCode.CAT_NO_CATALOG_FOR_GIVEN_DATE, requestedDate.toDate().toString());
         }
 
+        CatalogPlanEntry candidateInSubsequentCatalog = null;
         for (int i = catalogs.size() - 1; i >= 0; i--) { // Working backwards to find the latest applicable plan
             final StandaloneCatalog c = catalogs.get(i);
+
             final Plan plan;
             try {
                 plan = wrapper.findPlan(c);
             } catch (final CatalogApiException e) {
-                if (e.getCode() != ErrorCode.CAT_NO_SUCH_PLAN.getCode()) {
+                if (e.getCode() != ErrorCode.CAT_NO_SUCH_PLAN.getCode() &&
+                    e.getCode() != ErrorCode.CAT_PLAN_NOT_FOUND.getCode()) {
                     throw e;
                 } else {
                     // If we can't find an entry it probably means the plan has been retired so we keep looking...
@@ -179,21 +181,29 @@ public class VersionedCatalog extends ValidatingConfig<VersionedCatalog> impleme
             }
 
 
-            final boolean initialVersion = (i == 0);
+            final boolean oldestCatalog = (i == 0);
             final DateTime catalogEffectiveDate = CatalogDateHelper.toUTCDateTime(c.getEffectiveDate());
-            if (initialVersion || // Prevent issue with time granularity -- see #760
-                !subscriptionStartDate.isBefore(catalogEffectiveDate)) { // It's a new subscription this plan always applies
+            final boolean catalogOlderThanSubscriptionStartDate = !subscriptionStartDate.isBefore(catalogEffectiveDate);
+            if (oldestCatalog || // Prevent issue with time granularity -- see #760
+                catalogOlderThanSubscriptionStartDate) { // It's a new subscription, this plan always applies
                 return new CatalogPlanEntry(c, plan);
-            } else { //Its an existing subscription
-                if (plan.getEffectiveDateForExistingSubscriptions() != null) { //if it is null any change to this does not apply to existing subscriptions
+            } else { // It's an existing subscription
+                if (plan.getEffectiveDateForExistingSubscriptions() != null) { // If it is null, any change to this catalog does not apply to existing subscriptions
                     final DateTime existingSubscriptionDate = CatalogDateHelper.toUTCDateTime(plan.getEffectiveDateForExistingSubscriptions());
-                    if (requestedDate.isAfter(existingSubscriptionDate)) { // this plan is now applicable to existing subs
+                    if (requestedDate.isAfter(existingSubscriptionDate)) { // This plan is now applicable to existing subs
                         return new CatalogPlanEntry(c, plan);
                     }
+                } else if (candidateInSubsequentCatalog == null) {
+                    // Keep the most recent one
+                    candidateInSubsequentCatalog = new CatalogPlanEntry(c, plan);
                 }
             }
         }
 
+        if (candidateInSubsequentCatalog != null) {
+            return candidateInSubsequentCatalog;
+        }
+
         final PlanSpecifier spec = wrapper.getSpec();
         throw new CatalogApiException(ErrorCode.CAT_PLAN_NOT_FOUND,
                                       spec.getPlanName() != null ? spec.getPlanName() : "undefined",
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java b/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java
index f5513d6..b3f5bd0 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -27,19 +27,13 @@ import java.net.URL;
 import java.util.Iterator;
 import java.util.List;
 
-import javax.xml.bind.JAXBException;
-import javax.xml.transform.TransformerException;
-
 import org.joda.time.DateTime;
 import org.killbill.billing.catalog.CatalogTestSuiteNoDB;
 import org.killbill.billing.catalog.StandaloneCatalog;
-import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
-import org.killbill.billing.catalog.api.InvalidConfigException;
 import org.testng.Assert;
 import org.testng.annotations.Test;
-import org.xml.sax.SAXException;
 
 import com.google.common.io.Files;
 import com.google.common.io.Resources;
@@ -118,14 +112,16 @@ public class TestVersionedCatalogLoader extends CatalogTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testLoad() throws IOException, SAXException, InvalidConfigException, JAXBException, TransformerException, URISyntaxException, CatalogApiException {
+    public void testLoad() throws CatalogApiException {
         final VersionedCatalog c = loader.loadDefaultCatalog(Resources.getResource("versionedCatalog").toString());
-        Assert.assertEquals(c.size(), 3);
+        Assert.assertEquals(c.size(), 4);
         final Iterator<StandaloneCatalog> it = c.iterator();
         DateTime dt = new DateTime("2011-01-01T00:00:00+00:00");
         Assert.assertEquals(it.next().getEffectiveDate(), dt.toDate());
         dt = new DateTime("2011-02-02T00:00:00+00:00");
         Assert.assertEquals(it.next().getEffectiveDate(), dt.toDate());
+        dt = new DateTime("2011-02-03T00:00:00+00:00");
+        Assert.assertEquals(it.next().getEffectiveDate(), dt.toDate());
         dt = new DateTime("2011-03-03T00:00:00+00:00");
         Assert.assertEquals(it.next().getEffectiveDate(), dt.toDate());
     }
@@ -145,7 +141,7 @@ public class TestVersionedCatalogLoader extends CatalogTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testLoadCatalogFromInsideResourceFolder() throws CatalogApiException, URISyntaxException, IOException {
+    public void testLoadCatalogFromInsideResourceFolder() throws CatalogApiException {
         final VersionedCatalog c = loader.loadDefaultCatalog("com/acme/SpyCarCustom.xml");
         Assert.assertEquals(c.size(), 1);
         final DateTime dt = new DateTime("2015-10-04T00:00:00+00:00");
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java b/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
index a6fe002..4bcee55 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -22,15 +22,29 @@ import java.math.BigDecimal;
 
 import org.joda.time.DateTime;
 import org.killbill.billing.ErrorCode;
+import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
 
+    final DateTime dt0 = new DateTime("2010-01-01T00:00:00+00:00");
+    // WeaponsHireSmall-1.xml
+    final DateTime dt1 = new DateTime("2011-01-01T00:01:00+00:00");
+    // WeaponsHireSmall-2.xml
+    final DateTime dt2 = new DateTime("2011-02-02T00:01:00+00:00");
+    // WeaponsHireSmall-2a.xml
+    final DateTime dt2a = new DateTime("2011-02-03T00:01:00+00:00");
+    // effectiveDateForExistingSubscriptions from the catalogs 2 and 2a
+    final DateTime dt214 = new DateTime("2011-02-14T00:01:00+00:00");
+    // WeaponsHireSmall-3.xml
+    final DateTime dt3 = new DateTime("2011-03-03T00:01:00+00:00");
+
     private VersionedCatalog vc;
 
     @BeforeClass(groups = "fast")
@@ -43,16 +57,9 @@ public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
         vc = loader.loadDefaultCatalog("versionedCatalog");
     }
 
-
     @Test(groups = "fast")
     public void testFindPlanWithDates() throws Exception {
-        final DateTime dt0 = new DateTime("2010-01-01T00:00:00+00:00");
-        final DateTime dt1 = new DateTime("2011-01-01T00:01:00+00:00");
-        final DateTime dt2 = new DateTime("2011-02-02T00:01:00+00:00");
-        final DateTime dt214 = new DateTime("2011-02-14T00:01:00+00:00");
-        final DateTime dt3 = new DateTime("2011-03-03T00:01:00+00:00");
-
-        // We find it although the date provided is too early because we default to first catalog version
+        // We find it although the date provided is too early because we default to first catalog version (see also testErrorOnDateTooEarly below)
         final Plan newSubPlan0 = vc.findPlan("pistol-monthly", dt0, dt0);
 
         final Plan newSubPlan1 = vc.findPlan("pistol-monthly", dt1, dt1);
@@ -70,17 +77,66 @@ public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
         final Plan exSubPlan2 = vc.findPlan("pistol-monthly", dt2, dt1);
         final Plan exSubPlan214 = vc.findPlan("pistol-monthly", dt214, dt1);
         final Plan exSubPlan3 = vc.findPlan("pistol-monthly", dt3, dt1);
+        // Plan added in subsequent catalog (at dt2)
+        final Plan exSubPlan4 = vc.findPlan("shotgun-quarterly", dt2, dt1);
+        final Plan exSubPlan5 = vc.findPlan("shotgun-quarterly", dt2a, dt1);
+        final Plan exSubPlan6 = vc.findPlan("shotgun-quarterly", dt214, dt1);
+        final Plan exSubPlan7 = vc.findPlan("shotgun-quarterly", dt214, dt214);
 
         Assert.assertEquals(exSubPlan2.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("29.95"));
         Assert.assertEquals(exSubPlan214.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("39.95"));
         Assert.assertEquals(exSubPlan3.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("39.95"));
+        Assert.assertEquals(exSubPlan4.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("249.95"));
+        // Old price
+        Assert.assertEquals(exSubPlan5.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("249.95"));
+        // New price
+        Assert.assertEquals(exSubPlan6.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("259.95"));
+        Assert.assertEquals(exSubPlan7.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("259.95"));
+    }
+
+    // Similar to testFindPlanWithDates, but use the API with PlanSpecifier
+    @Test(groups = "fast")
+    public void testFindPlanWithDatesAndPlanSpecifier() throws Exception {
+        final PlanSpecifier pistolMonthly = new PlanSpecifier("Pistol", BillingPeriod.MONTHLY, "DEFAULT");
+        final PlanSpecifier shotgunQuarterly = new PlanSpecifier("Shotgun", BillingPeriod.QUARTERLY, "DEFAULT");
+
+        // We find it although the date provided is too early because we default to first catalog version (see also testErrorOnDateTooEarly below)
+        final Plan newSubPlan0 = vc.createOrFindPlan(pistolMonthly, null, dt0, dt0);
+
+        final Plan newSubPlan1 = vc.createOrFindPlan(pistolMonthly, null, dt1, dt1);
+        final Plan newSubPlan2 = vc.createOrFindPlan(pistolMonthly, null, dt2, dt2);
+        final Plan newSubPlan214 = vc.createOrFindPlan(pistolMonthly, null, dt214, dt214);
+        final Plan newSubPlan3 = vc.createOrFindPlan(pistolMonthly, null, dt3, dt3);
 
+        Assert.assertEquals(newSubPlan1.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("29.95"));
+        Assert.assertEquals(newSubPlan2.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("39.95"));
+        Assert.assertEquals(newSubPlan214.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("39.95"));
+        Assert.assertEquals(newSubPlan3.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("49.95"));
+
+        // Existing subscription
+
+        final Plan exSubPlan2 = vc.createOrFindPlan(pistolMonthly, null, dt2, dt1);
+        final Plan exSubPlan214 = vc.createOrFindPlan(pistolMonthly, null, dt214, dt1);
+        final Plan exSubPlan3 = vc.createOrFindPlan(pistolMonthly, null, dt3, dt1);
+        // Plan added in subsequent catalog (at dt2)
+        final Plan exSubPlan4 = vc.createOrFindPlan(shotgunQuarterly, null, dt2, dt1);
+        final Plan exSubPlan5 = vc.createOrFindPlan(shotgunQuarterly, null, dt2a, dt1);
+        final Plan exSubPlan6 = vc.createOrFindPlan(shotgunQuarterly, null, dt214, dt1);
+        final Plan exSubPlan7 = vc.createOrFindPlan(shotgunQuarterly, null, dt214, dt214);
+
+        Assert.assertEquals(exSubPlan2.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("29.95"));
+        Assert.assertEquals(exSubPlan214.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("39.95"));
+        Assert.assertEquals(exSubPlan3.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("39.95"));
+        Assert.assertEquals(exSubPlan4.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("249.95"));
+        // Old price
+        Assert.assertEquals(exSubPlan5.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("249.95"));
+        // New price
+        Assert.assertEquals(exSubPlan6.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("259.95"));
+        Assert.assertEquals(exSubPlan7.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("259.95"));
     }
 
     @Test(groups = "fast")
     public void testErrorOnDateTooEarly() throws CatalogApiException {
-        final DateTime dt0 = new DateTime("1977-01-01T00:00:00+00:00");
-
         // We find it although the date provided is too early because we default to first catalog version
         vc.findPlan("shotgun-monthly", dt0);
 
@@ -88,25 +144,26 @@ public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
             // We **don't find it** because date is too early and not part of first catalog version
             vc.findPlan("shotgun-quarterly", dt0);
             Assert.fail("Date is too early an exception should have been thrown");
-        } catch (CatalogApiException e) {
+        } catch (final CatalogApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.CAT_NO_SUCH_PLAN.getCode());
         }
     }
 
-
     @Test(groups = "fast")
     public void testWithDeletedPlan() throws CatalogApiException {
-
         // We find it because this is version 2 whose effectiveDate is "2011-02-02T00:00:00+00:00"
-        vc.findPlan("shotgun-quarterly", new DateTime("2011-02-02T00:01:00+00:00"));
+        vc.findPlan("shotgun-quarterly", dt2);
 
         try {
             // We **don't find it** because date provided matches version 3 where plan was removed
-            vc.findPlan("shotgun-quarterly", new DateTime("2011-03-03T00:01:00+00:00"));
+            vc.findPlan("shotgun-quarterly", dt3);
             Assert.fail("Plan has been removed");
-        } catch (CatalogApiException e) {
+        } catch (final CatalogApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.CAT_NO_SUCH_PLAN.getCode());
         }
 
+        // Similar test but for existing subscription: we want to find the plan in the original catalog in this case.
+        // This would be called for instance when computing billing events (dt3 could be a future PHASE event for instance)
+        vc.findPlan("shotgun-quarterly", dt3, dt1);
     }
 }
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
index f333572..5f1a214 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
@@ -1,8 +1,10 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!--
   ~ Copyright 2010-2013 Ning, Inc.
+  ~ Copyright 2014-2018 Groupon, Inc
+  ~ Copyright 2014-2018 The Billing Project, LLC
   ~
-  ~ Ning licenses this file to you under the Apache License, version 2.0
+  ~ 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:
   ~
@@ -16,7 +18,7 @@
   -->
 
 <catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+         xsi:noNamespaceSchemaLocation="http://docs.killbill.io/latest/catalog.xsd">
 
     <effectiveDate>2011-01-01T00:00:00+00:00</effectiveDate>
     <catalogName>WeaponsHireSmall</catalogName>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
index 3fee3ea..0e11d5b 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
@@ -1,8 +1,10 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!--
   ~ Copyright 2010-2013 Ning, Inc.
+  ~ Copyright 2014-2018 Groupon, Inc
+  ~ Copyright 2014-2018 The Billing Project, LLC
   ~
-  ~ Ning licenses this file to you under the Apache License, version 2.0
+  ~ 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:
   ~
@@ -15,8 +17,16 @@
   ~ under the License.
   -->
 
+<!---
+
+Changes compared to WeaponsHireSmall-1.xml:
+  * default change policy IMMEDIATE
+  * pistol-monthly price change
+  * new shotgun-quarterly plan
+
+-->
 <catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+         xsi:noNamespaceSchemaLocation="http://docs.killbill.io/latest/catalog.xsd">
 
     <effectiveDate>2011-02-02T00:00:00+00:00</effectiveDate>
     <catalogName>WeaponsHireSmall</catalogName>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2a.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2a.xml
new file mode 100644
index 0000000..c2ecfd6
--- /dev/null
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2a.xml
@@ -0,0 +1,343 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ Copyright 2010-2013 Ning, Inc.
+  ~ Copyright 2014-2018 Groupon, Inc
+  ~ Copyright 2014-2018 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.
+  -->
+
+<!---
+
+Changes compared to WeaponsHireSmall-2.xml:
+  * shotgun-quarterly price change
+
+-->
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="http://docs.killbill.io/latest/catalog.xsd">
+
+    <effectiveDate>2011-02-03T00:00:00+00:00</effectiveDate>
+    <catalogName>WeaponsHireSmall</catalogName>
+
+    <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
+
+    <currencies>
+        <currency>USD</currency>
+        <currency>EUR</currency>
+        <currency>GBP</currency>
+    </currencies>
+
+    <units>
+        <unit name="targets"/>
+        <unit name="misfires"/>
+        <unit name="shells"/>
+    </units>
+
+    <products>
+        <product name="Pistol">
+            <category>BASE</category>
+        </product>
+        <product name="Shotgun">
+            <category>BASE</category>
+            <limits>
+                <limit>
+                    <unit>shells</unit>
+                    <max>300</max>
+                </limit>
+            </limits>
+        </product>
+        <product name="Laser-Scope">
+            <category>ADD_ON</category>
+        </product>
+        <product name="Extra-Ammo">
+            <category>ADD_ON</category>
+        </product>
+    </products>
+
+    <rules>
+        <changePolicy>
+            <changePolicyCase>
+                <fromBillingPeriod>MONTHLY</fromBillingPeriod>
+                <toProduct>Shotgun</toProduct>
+                <toBillingPeriod>MONTHLY</toBillingPeriod>
+                <policy>END_OF_TERM</policy>
+            </changePolicyCase>
+            <changePolicyCase>
+                <phaseType>TRIAL</phaseType>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+            <changePolicyCase>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+        </changePolicy>
+        <changeAlignment>
+            <changeAlignmentCase>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </changeAlignmentCase>
+        </changeAlignment>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
+        <createAlignment>
+            <createAlignmentCase>
+                <product>Laser-Scope</product>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </createAlignmentCase>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+        </createAlignment>
+    </rules>
+
+    <plans>
+        <plan name="pistol-monthly">
+            <effectiveDateForExistingSubscriptions>2011-02-14T00:00:00+00:00</effectiveDateForExistingSubscriptions>
+
+            <product>Pistol</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>39.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>39.95</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>39.95</value>
+                        </price>
+                    </recurringPrice>
+                    <!--
+                    <limits>
+                        <limit>
+                            <unit>targets</unit>
+                            <min>3</min>
+                        </limit>
+                        <limit>
+                            <unit>misfires</unit>
+                            <max>20</max>
+                        </limit>
+                    </limits>
+                    -->
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="shotgun-monthly">
+            <product>Shotgun</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                    <number>-1</number>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>249.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>149.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>169.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="shotgun-quarterly">
+            <effectiveDateForExistingSubscriptions>2011-02-14T00:00:00+00:00</effectiveDateForExistingSubscriptions>
+
+            <product>Shotgun</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                    <number>-1</number>
+                </duration>
+                <recurring>
+                    <billingPeriod>QUARTERLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>259.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>159.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>179.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="shotgun-annual">
+            <product>Shotgun</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>2399.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>1499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>1699.95</value>
+                        </price>
+                    </recurringPrice>
+                    <!--
+                    <limits>
+                        <limit>
+                            <unit>shells</unit>
+                            <max>200</max>
+                        </limit>
+                    </limits>
+                    -->
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="laser-scope-monthly">
+            <product>Laser-Scope</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>1999.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>1499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>1999.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="extra-ammo-monthly">
+            <product>Extra-Ammo</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>1999.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>1499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>1999.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+    </plans>
+    <priceLists>
+        <defaultPriceList name="DEFAULT">
+            <plans>
+                <plan>pistol-monthly</plan>
+                <plan>shotgun-monthly</plan>
+                <plan>shotgun-quarterly</plan>
+                <plan>shotgun-annual</plan>
+                <plan>laser-scope-monthly</plan>
+                <plan>extra-ammo-monthly</plan>
+            </plans>
+        </defaultPriceList>
+    </priceLists>
+</catalog>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
index eb21229..779a440 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
@@ -1,8 +1,10 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!--
   ~ Copyright 2010-2013 Ning, Inc.
+  ~ Copyright 2014-2018 Groupon, Inc
+  ~ Copyright 2014-2018 The Billing Project, LLC
   ~
-  ~ Ning licenses this file to you under the Apache License, version 2.0
+  ~ 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:
   ~
@@ -15,8 +17,15 @@
   ~ under the License.
   -->
 
+<!---
+
+Changes compared to WeaponsHireSmall-2a.xml:
+  * pistol-monthly price change
+  * shotgun-quarterly plan retired
+
+-->
 <catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+         xsi:noNamespaceSchemaLocation="http://docs.killbill.io/latest/catalog.xsd">
 
     <effectiveDate>2011-03-03T00:00:00+00:00</effectiveDate>
     <catalogName>WeaponsHireSmall</catalogName>

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

diff --git a/currency/pom.xml b/currency/pom.xml
index 7a97157..623f640 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-currency</artifactId>
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 31beb3d..c8645c9 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java b/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java
index 3039f4c..28e957a 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java
@@ -248,7 +248,7 @@ public abstract class EntitlementLoggingHelper {
             }
             if (spec.getPriceListName() != null) {
                 logLine.append(", priceList='")
-                       .append(spec.getBillingPeriod())
+                       .append(spec.getPriceListName())
                        .append("'");
             }
             logPlanPhasePriceOverrides(logLine, overrides);

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

diff --git a/invoice/pom.xml b/invoice/pom.xml
index fa34ea0..8d13d6f 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>
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 746cdf1..ecb7c41 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
@@ -20,7 +20,6 @@ package org.killbill.billing.invoice.generator;
 import java.math.BigDecimal;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -28,7 +27,6 @@ import javax.annotation.Nullable;
 
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.BillingMode;
-import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.model.DefaultInvoice;
@@ -46,7 +44,7 @@ public class InvoiceWithMetadata {
         this.invoice = originalInvoice;
         this.perSubscriptionFutureNotificationDates = perSubscriptionFutureNotificationDates;
         build();
-        remove$0UsageItems();
+        removeMarkerUsageItems();
     }
 
     public DefaultInvoice getInvoice() {
@@ -80,13 +78,12 @@ public class InvoiceWithMetadata {
         });
     }
 
-    protected void remove$0UsageItems() {
+    protected void removeMarkerUsageItems() {
         if (invoice != null) {
             final Iterator<InvoiceItem> it = invoice.getInvoiceItems().iterator();
             while (it.hasNext()) {
                 final InvoiceItem item = it.next();
-                if ((item.getInvoiceItemType() == InvoiceItemType.USAGE) &&
-                    item.getAmount().compareTo(BigDecimal.ZERO) == 0) {
+                if (isMarkerUsageItem(item)) {
                     it.remove();
                 }
             }
@@ -96,6 +93,16 @@ public class InvoiceWithMetadata {
         }
     }
 
+    //
+    // $0 Usage item with no detail section
+    // ($0 Usage item *with* detail section are valid items in case of tiers defined with $0 amount)
+    //
+    private boolean isMarkerUsageItem(final InvoiceItem item) {
+        return item.getInvoiceItemType() == InvoiceItemType.USAGE &&
+               item.getItemDetails() == null &&
+               item.getAmount().compareTo(BigDecimal.ZERO) == 0;
+    }
+
 
     public static class SubscriptionFutureNotificationDates {
 

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

diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index de0d67a..f5a8c19 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-jaxrs</artifactId>
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
index bdf5a1c..adb0acb 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
@@ -122,8 +122,12 @@ public class CatalogResource extends JaxRsResourceBase {
     @ApiOperation(value = "Retrieve the full catalog as XML", response = String.class, hidden = true)
     @ApiResponses(value = {})
     public Response getCatalogXmlOriginal(@QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
+                                          @QueryParam(QUERY_ACCOUNT_ID) final UUID accountId,
                                           @javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
-        final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
+        final TenantContext tenantContext = accountId != null ?
+                                            context.createTenantContextWithAccountId(accountId, request) :
+                                            context.createTenantContextNoAccountId(request);
+
         final DateTime catalogDateVersion = requestedDate != null ?
                                             DATE_TIME_FORMATTER.parseDateTime(requestedDate).toDateTime(DateTimeZone.UTC) :
                                             null;
@@ -153,8 +157,9 @@ public class CatalogResource extends JaxRsResourceBase {
     @ApiOperation(value = "Retrieve the full catalog as XML", response = String.class)
     @ApiResponses(value = {})
     public Response getCatalogXml(@QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
-        @javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
-        return getCatalogXmlOriginal(requestedDate, request);
+                                  @QueryParam(QUERY_ACCOUNT_ID) final UUID accountId,
+                                  @javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
+        return getCatalogXmlOriginal(requestedDate, accountId, request);
     }
 
 
@@ -197,9 +202,12 @@ public class CatalogResource extends JaxRsResourceBase {
     @ApiOperation(value = "Retrieve the catalog as JSON", responseContainer = "List", response = CatalogJson.class)
     @ApiResponses(value = {})
     public Response getCatalogJson(@QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
+                                   @QueryParam(QUERY_ACCOUNT_ID) final UUID accountId,
                                    @javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
 
-        final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
+        final TenantContext tenantContext = accountId != null ?
+                                            context.createTenantContextWithAccountId(accountId, request) :
+                                            context.createTenantContextNoAccountId(request);
         final DateTime catalogDateVersion = requestedDate != null ?
                                             DATE_TIME_FORMATTER.parseDateTime(requestedDate).toDateTime(DateTimeZone.UTC) :
                                             null;
@@ -224,9 +232,11 @@ public class CatalogResource extends JaxRsResourceBase {
     @Produces(APPLICATION_JSON)
     @ApiOperation(value = "Retrieve a list of catalog versions", response = DateTime.class, responseContainer = "List")
     @ApiResponses(value = {})
-    public Response getCatalogVersions(@javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
-
-        final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
+    public Response getCatalogVersions(@QueryParam(QUERY_ACCOUNT_ID) final UUID accountId,
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
+        final TenantContext tenantContext = accountId != null ?
+                                            context.createTenantContextWithAccountId(accountId, request) :
+                                            context.createTenantContextNoAccountId(request);
         final VersionedCatalog catalog = (VersionedCatalog) catalogUserApi.getCatalog(catalogName, tenantContext);
 
         final List<DateTime> result = new ArrayList<DateTime>();
@@ -259,8 +269,13 @@ public class CatalogResource extends JaxRsResourceBase {
     @ApiResponses(value = {})
     public Response getAvailableAddons(@QueryParam("baseProductName") final String baseProductName,
                                        @Nullable @QueryParam("priceListName") final String priceListName,
+                                       @QueryParam(QUERY_ACCOUNT_ID) final UUID accountId,
                                        @javax.ws.rs.core.Context final HttpServletRequest request) throws CatalogApiException {
-        final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
+
+        final TenantContext tenantContext = accountId != null ?
+                                            context.createTenantContextWithAccountId(accountId, request) :
+                                            context.createTenantContextNoAccountId(request);
+
         final StaticCatalog catalog = catalogUserApi.getCurrentCatalog(catalogName, tenantContext);
         final List<Listing> listings = catalog.getAvailableAddOnListings(baseProductName, priceListName);
         final List<PlanDetailJson> details = new ArrayList<PlanDetailJson>();
@@ -276,8 +291,12 @@ public class CatalogResource extends JaxRsResourceBase {
     @Produces(APPLICATION_JSON)
     @ApiOperation(value = "Retrieve available base plans", response = PlanDetailJson.class, responseContainer = "List")
     @ApiResponses(value = {})
-    public Response getAvailableBasePlans(@javax.ws.rs.core.Context final HttpServletRequest request) throws CatalogApiException {
-        final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
+    public Response getAvailableBasePlans(@QueryParam(QUERY_ACCOUNT_ID) final UUID accountId,
+                                          @javax.ws.rs.core.Context final HttpServletRequest request) throws CatalogApiException {
+        final TenantContext tenantContext = accountId != null ?
+                                            context.createTenantContextWithAccountId(accountId, request) :
+                                            context.createTenantContextNoAccountId(request);
+
         final StaticCatalog catalog = catalogUserApi.getCurrentCatalog(catalogName, tenantContext);
         final List<Listing> listings = catalog.getAvailableBasePlanListings();
         final List<PlanDetailJson> details = new ArrayList<PlanDetailJson>();

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

diff --git a/junction/pom.xml b/junction/pom.xml
index 5915349..abb20ff 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-junction</artifactId>

NEWS 5(+5 -0)

diff --git a/NEWS b/NEWS
index a53274a..03d294e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+0.19.15
+    Fix issue in usage billing (DETAIL mode)
+    Catalog endpoints enhancements
+    Merge fix from 0.18.x to allow to change to a plan defined in a subsequent catalog version
+
 0.19.14
     Expose and persist invoice item product name
     New history apis

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

diff --git a/overdue/pom.xml b/overdue/pom.xml
index 769323d..7bc3332 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-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 2a02951..04558c9 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>

pom.xml 4(+2 -2)

diff --git a/pom.xml b/pom.xml
index 11416a4..1340f45 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,10 +21,10 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.141.65</version>
+        <version>0.141.67</version>
     </parent>
     <artifactId>killbill</artifactId>
-    <version>0.19.15-SNAPSHOT</version>
+    <version>0.19.16-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 a4436b7..e169b28 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles-killbill</artifactId>
diff --git a/profiles/killpay/pom.xml b/profiles/killpay/pom.xml
index 5c3e213..fbb4bca 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-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 4e63936..7be834c 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles</artifactId>
diff --git a/subscription/pom.xml b/subscription/pom.xml
index 6bab51f..9c2bd48 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-subscription</artifactId>
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestDefaultSubscriptionBase.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestDefaultSubscriptionBase.java
index 9dff853..fab5df1 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestDefaultSubscriptionBase.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestDefaultSubscriptionBase.java
@@ -41,7 +41,8 @@ public class TestDefaultSubscriptionBase extends SubscriptionTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testCancelSOT() throws Exception {
-        final DefaultSubscriptionBase subscriptionBase = new DefaultSubscriptionBase(new SubscriptionBuilder());
+        final DateTime startDate = new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC);
+        final DefaultSubscriptionBase subscriptionBase = new DefaultSubscriptionBase(new SubscriptionBuilder().setAlignStartDate(startDate));
 
         final UUID subscriptionId = UUID.randomUUID();
         final List<SubscriptionBaseEvent> inputEvents = new LinkedList<SubscriptionBaseEvent>();
@@ -52,9 +53,9 @@ public class TestDefaultSubscriptionBase extends SubscriptionTestSuiteNoDB {
                                                                 .setFromDisk(true)
                                                                 .setUuid(UUID.randomUUID())
                                                                 .setSubscriptionId(subscriptionId)
-                                                                .setCreatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
-                                                                .setUpdatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
-                                                                .setEffectiveDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
+                                                                .setCreatedDate(startDate)
+                                                                .setUpdatedDate(startDate)
+                                                                .setEffectiveDate(startDate)
                                                                 .setTotalOrdering(3)
                                                                 .setActive(true)));
         inputEvents.add(new ApiEventCancel(new ApiEventBuilder().setApiEventType(ApiEventType.CANCEL)
@@ -64,9 +65,9 @@ public class TestDefaultSubscriptionBase extends SubscriptionTestSuiteNoDB {
                                                                 .setFromDisk(false)
                                                                 .setUuid(UUID.randomUUID())
                                                                 .setSubscriptionId(subscriptionId)
-                                                                .setCreatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
+                                                                .setCreatedDate(startDate)
                                                                 .setUpdatedDate(null)
-                                                                .setEffectiveDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
+                                                                .setEffectiveDate(startDate)
                                                                 .setTotalOrdering(0) // In-memory event
                                                                 .setActive(true)));
         subscriptionBase.rebuildTransitions(inputEvents, catalog);
@@ -74,15 +75,16 @@ public class TestDefaultSubscriptionBase extends SubscriptionTestSuiteNoDB {
         Assert.assertEquals(subscriptionBase.getAllTransitions().size(), 2);
         Assert.assertNull(subscriptionBase.getAllTransitions().get(0).getPreviousState());
         Assert.assertEquals(subscriptionBase.getAllTransitions().get(0).getNextState(), EntitlementState.ACTIVE);
-        Assert.assertEquals(subscriptionBase.getAllTransitions().get(0).getEffectiveTransitionTime(), new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC));
+        Assert.assertEquals(subscriptionBase.getAllTransitions().get(0).getEffectiveTransitionTime(), startDate);
         Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getPreviousState(), EntitlementState.ACTIVE);
         Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getNextState(), EntitlementState.CANCELLED);
-        Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getEffectiveTransitionTime(), new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC));
+        Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getEffectiveTransitionTime(), startDate);
     }
 
     @Test(groups = "fast", description = "https://github.com/killbill/killbill/issues/897")
     public void testFutureCancelBeforePhase() throws Exception {
-        final DefaultSubscriptionBase subscriptionBase = new DefaultSubscriptionBase(new SubscriptionBuilder());
+        final DateTime startDate = new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC);
+        final DefaultSubscriptionBase subscriptionBase = new DefaultSubscriptionBase(new SubscriptionBuilder().setAlignStartDate(startDate));
 
         final UUID subscriptionId = UUID.randomUUID();
         final List<SubscriptionBaseEvent> inputEvents = new LinkedList<SubscriptionBaseEvent>();
@@ -93,16 +95,16 @@ public class TestDefaultSubscriptionBase extends SubscriptionTestSuiteNoDB {
                                                                 .setFromDisk(true)
                                                                 .setUuid(UUID.randomUUID())
                                                                 .setSubscriptionId(subscriptionId)
-                                                                .setCreatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
-                                                                .setUpdatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
-                                                                .setEffectiveDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
+                                                                .setCreatedDate(startDate)
+                                                                .setUpdatedDate(startDate)
+                                                                .setEffectiveDate(startDate)
                                                                 .setTotalOrdering(3)
                                                                 .setActive(true)));
         inputEvents.add(new PhaseEventData(new PhaseEventBuilder().setPhaseName("laser-scope-monthly-evergreen")
                                                                   .setUuid(UUID.randomUUID())
                                                                   .setSubscriptionId(subscriptionId)
-                                                                  .setCreatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
-                                                                  .setUpdatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
+                                                                  .setCreatedDate(startDate)
+                                                                  .setUpdatedDate(startDate)
                                                                   .setEffectiveDate(new DateTime(2012, 6, 1, 0, 0, DateTimeZone.UTC))
                                                                   .setTotalOrdering(4)
                                                                   .setActive(true)));
@@ -113,7 +115,7 @@ public class TestDefaultSubscriptionBase extends SubscriptionTestSuiteNoDB {
                                                                 .setFromDisk(false)
                                                                 .setUuid(UUID.randomUUID())
                                                                 .setSubscriptionId(subscriptionId)
-                                                                .setCreatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
+                                                                .setCreatedDate(startDate)
                                                                 .setUpdatedDate(null)
                                                                 .setEffectiveDate(new DateTime(2012, 6, 1, 0, 0, DateTimeZone.UTC))
                                                                 .setTotalOrdering(0) // In-memory event
@@ -123,7 +125,7 @@ public class TestDefaultSubscriptionBase extends SubscriptionTestSuiteNoDB {
         Assert.assertEquals(subscriptionBase.getAllTransitions().size(), 2);
         Assert.assertNull(subscriptionBase.getAllTransitions().get(0).getPreviousState());
         Assert.assertEquals(subscriptionBase.getAllTransitions().get(0).getNextState(), EntitlementState.ACTIVE);
-        Assert.assertEquals(subscriptionBase.getAllTransitions().get(0).getEffectiveTransitionTime(), new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC));
+        Assert.assertEquals(subscriptionBase.getAllTransitions().get(0).getEffectiveTransitionTime(), startDate);
         Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getPreviousState(), EntitlementState.ACTIVE);
         Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getNextState(), EntitlementState.CANCELLED);
         Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getEffectiveTransitionTime(), new DateTime(2012, 6, 1, 0, 0, DateTimeZone.UTC));

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

diff --git a/tenant/pom.xml b/tenant/pom.xml
index 3eb796b..4696115 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-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 004bc90..f8bf1e1 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-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 9c8d674..faa7cc2 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.19.15-SNAPSHOT</version>
+        <version>0.19.16-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>