killbill-memoizeit

Changes

pom.xml 2(+1 -1)

Details

diff --git a/api/src/main/java/org/killbill/billing/catalog/api/CatalogService.java b/api/src/main/java/org/killbill/billing/catalog/api/CatalogService.java
index f461328..b55c64b 100644
--- a/api/src/main/java/org/killbill/billing/catalog/api/CatalogService.java
+++ b/api/src/main/java/org/killbill/billing/catalog/api/CatalogService.java
@@ -27,7 +27,7 @@ import org.killbill.billing.util.callcontext.TenantContext;
  */
 public interface CatalogService extends KillbillService {
 
-    public Catalog getFullCatalog(InternalTenantContext context) throws CatalogApiException;
+    public Catalog getFullCatalog(boolean useDefaultCatalog, InternalTenantContext context) throws CatalogApiException;
 
-    public StaticCatalog getCurrentCatalog(InternalTenantContext context) throws CatalogApiException;
+    public StaticCatalog getCurrentCatalog(boolean useDefaultCatalog, InternalTenantContext context) throws CatalogApiException;
 }
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java b/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java
index 434b964..878b789 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java
@@ -24,6 +24,7 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.util.entity.Entity;
 
@@ -60,6 +61,12 @@ public interface SubscriptionBaseTimeline extends Entity {
         public SubscriptionBaseTransitionType getSubscriptionTransitionType();
 
         /**
+         *
+         * @return the product category
+         */
+        public ProductCategory getProductCategory();
+
+        /**
          * @return the date at which this event was effective
          */
         public DateTime getEffectiveDate();
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
index 648d522..413d676 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
@@ -166,7 +166,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
         //
         // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        final PlanPhaseSpecifier bpPlanPhaseSpecifier = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null);
+        final PlanPhaseSpecifier bpPlanPhaseSpecifier = new PlanPhaseSpecifier(productName, term, planSetName, null);
 
         final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
index 6d98f81..919890e 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
@@ -79,7 +79,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
 
         // Catalog v2 should start now.
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, term, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         try {
             entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
             fail(); // force to fail is there is not an exception
@@ -135,7 +135,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
 
         // Catalog v3 should start now.
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, term, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         try {
             entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
             fail(); // force to fail is there is not an exception
@@ -170,7 +170,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
 
         final String productName = "Pistol";
         final BillingPeriod term = BillingPeriod.MONTHLY;
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, "SpecialDiscount", null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, term, "SpecialDiscount", null);
 
         busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         final Entitlement bpEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey", null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index 50af600..59a72b9 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -606,7 +606,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
             @Override
             public Entitlement apply(@Nullable final Void dontcare) {
                 try {
-                    final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, productCategory, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+                    final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);
                     final Entitlement entitlement = entitlementApi.createBaseEntitlement(accountId, spec, bundleExternalKey, overrides, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
                     assertNotNull(entitlement);
                     return entitlement;
@@ -640,7 +640,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
             @Override
             public Entitlement apply(@Nullable final Void dontcare) {
                 try {
-                    final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, productCategory, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+                    final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);
                     final Entitlement entitlement = entitlementApi.addEntitlement(bundleId, spec, null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
                     assertNotNull(entitlement);
                     return entitlement;
@@ -808,7 +808,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
                                    final LocalDate effectiveDate,
                                    final BillingActionPolicy billingPolicy) {
             this.dryRunType = dryRunType;
-            this.spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceList, phaseType);
+            this.spec = new PlanPhaseSpecifier(productName, billingPeriod, priceList, phaseType);
             this.action = action;
             this.subscriptionId = subscriptionId;
             this.bundleId = bundleId;
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java
index 2b9462e..7724af0 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java
@@ -31,6 +31,7 @@ import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.DefaultEntitlement;
 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.api.InvoiceStatus;
 import org.killbill.billing.payment.api.Payment;
@@ -121,6 +122,16 @@ public class TestIntegrationParentInvoice extends TestIntegrationBase {
         assertTrue(parentInvoice.getBalance().compareTo(BigDecimal.ZERO) == 0);
         assertTrue(child1Invoices.get(1).getBalance().compareTo(BigDecimal.ZERO) == 0);
 
+        // load children invoice items
+        final List<InvoiceItem> childrenInvoiceItems = invoiceUserApi.getInvoiceItemsByParentInvoice(parentInvoice.getId(), callContext);
+        assertEquals(childrenInvoiceItems.size(), 2);
+        assertEquals(childrenInvoiceItems.get(0).getAccountId(), child1Account.getId());
+        assertEquals(childrenInvoiceItems.get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(childrenInvoiceItems.get(1).getAccountId(), child2Account.getId());
+        assertEquals(childrenInvoiceItems.get(1).getAmount().compareTo(BigDecimal.valueOf(29.95)), 0);
+
+        // loading children items from non parent account should return empty list
+        assertEquals(invoiceUserApi.getInvoiceItemsByParentInvoice(child1Invoices.get(1).getId(), callContext).size(), 0);
     }
 
     @Test(groups = "slow")
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithCatalogUpdate.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithCatalogUpdate.java
new file mode 100644
index 0000000..8e8c6e2
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithCatalogUpdate.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.beatrix.integration;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountData;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
+import org.killbill.billing.callcontext.DefaultCallContext;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.CatalogUserApi;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.SimplePlanDescriptor;
+import org.killbill.billing.catalog.api.StaticCatalog;
+import org.killbill.billing.catalog.api.TimeUnit;
+import org.killbill.billing.catalog.api.user.DefaultSimplePlanDescriptor;
+import org.killbill.billing.entitlement.api.Entitlement;
+import org.killbill.billing.entitlement.api.EntitlementApiException;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.payment.api.PaymentMethodPlugin;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.tenant.api.DefaultTenant;
+import org.killbill.billing.tenant.api.Tenant;
+import org.killbill.billing.tenant.api.TenantApiException;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.CallOrigin;
+import org.killbill.billing.util.callcontext.UserType;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+public class TestIntegrationWithCatalogUpdate extends TestIntegrationBase {
+
+    @Inject
+    private CatalogUserApi catalogUserApi;
+
+    private Tenant tenant;
+    private CallContext testCallContext;
+    private Account account;
+
+    private DateTime init;
+
+    @BeforeMethod(groups = "slow")
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
+
+        // Set original time
+        clock.setDay(new LocalDate(2016, 6, 1));
+        init = clock.getUTCNow();
+
+        // Setup tenant
+        setupTenant();
+
+        // Setup account in right tenant
+        setupAccount();
+    }
+
+    @Test(groups = "slow")
+    public void testBasic() throws Exception {
+
+        // Create a per-tenant catalog with one plan
+        final SimplePlanDescriptor desc1 = new DefaultSimplePlanDescriptor("foo-monthly", "Foo", account.getCurrency(), BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED);
+        catalogUserApi.addSimplePlan(desc1, init, testCallContext);
+        StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
+        assertEquals(catalog.getCurrentPlans().length, 1);
+
+        final Entitlement baseEntitlement = createEntitlement("Foo", BillingPeriod.MONTHLY);
+
+        invoiceChecker.checkInvoice(account.getId(), 1, testCallContext, new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 1), new LocalDate(2016, 7, 1), InvoiceItemType.RECURRING, BigDecimal.TEN));
+
+        // Add another Plan in the catalog
+        final SimplePlanDescriptor desc2 = new DefaultSimplePlanDescriptor("superfoo-monthly", "SuperFoo", account.getCurrency(), new BigDecimal("20.00"), BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED);
+        catalogUserApi.addSimplePlan(desc2, init, testCallContext);
+        catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
+        assertEquals(catalog.getCurrentPlans().length, 2);
+
+
+        // Change Plan to the newly added Plan and verify correct default rules behavior (IMMEDIATE change)
+        busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+        baseEntitlement.changePlan("SuperFoo", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null, ImmutableList.<PluginProperty>of(), testCallContext);
+        assertListenerStatus();
+
+        invoiceChecker.checkInvoice(account.getId(), 2, testCallContext,
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 1), new LocalDate(2016, 7, 1), InvoiceItemType.RECURRING, new BigDecimal("20.00")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 1), new LocalDate(2016, 7, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-10.00")));
+        assertListenerStatus();
+    }
+
+
+    private Entitlement createEntitlement(String productName, BillingPeriod billingPeriod) throws EntitlementApiException {
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, null, null, false, ImmutableList.<PluginProperty>of(), testCallContext);
+        assertListenerStatus();
+        return entitlement;
+    }
+
+
+    private void setupTenant() throws TenantApiException {
+        final UUID uuid = UUID.randomUUID();
+        final String externalKey = uuid.toString();
+        final String apiKey = externalKey + "-Key";
+        final String apiSecret = externalKey + "-$3cr3t";
+        // Only place where we use callContext
+        tenant = tenantUserApi.createTenant(new DefaultTenant(uuid, init, init, externalKey, apiKey, apiSecret), callContext);
+
+        testCallContext = new DefaultCallContext(tenant.getId(), "tester", CallOrigin.EXTERNAL, UserType.TEST,
+                                      "good reason", "trust me", uuid, clock);
+    }
+
+    private void setupAccount() throws Exception {
+
+        final AccountData accountData = getAccountData(1);
+        account = accountUserApi.createAccount(accountData, testCallContext);
+        assertNotNull(account);
+
+        final PaymentMethodPlugin info = createPaymentMethodPlugin();
+        paymentApi.addPaymentMethod(account, UUID.randomUUID().toString(),  BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME, true, info, PLUGIN_PROPERTIES, testCallContext);
+    }
+
+}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestMigrationSubscriptions.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestMigrationSubscriptions.java
index cdd69e5..3069314 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestMigrationSubscriptions.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestMigrationSubscriptions.java
@@ -30,7 +30,6 @@ import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.PriceListSet;
-import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier;
 import org.killbill.billing.entitlement.api.Entitlement;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
@@ -72,7 +71,7 @@ public class TestMigrationSubscriptions extends TestIntegrationBase {
         final LocalDate billingMigrationDate = new LocalDate(2016, 1, 31);
 
         // Entitlement wil be created in PENDING state
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "bundleKey", null, entitlementMigrationDate, billingMigrationDate, false, ImmutableList.<PluginProperty>of(), callContext);
         Assert.assertEquals(entitlement.getState(), EntitlementState.PENDING);
 
@@ -128,7 +127,7 @@ public class TestMigrationSubscriptions extends TestIntegrationBase {
 
         // Entitlement wil be created in ACTIVE state because entitlementMigrationDate was set in the past
         busHandler.pushExpectedEvents(NextEvent.BLOCK);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "bundleKey", null, entitlementMigrationDate, billingMigrationDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -173,7 +172,7 @@ public class TestMigrationSubscriptions extends TestIntegrationBase {
 
         // Entitlement wil be created in ACTIVE state because entitlementMigrationDate was set in the past
         busHandler.pushExpectedEvents(NextEvent.BLOCK);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "bundleKey", null, entitlementMigrationDate, billingMigrationDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
         Assert.assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
@@ -224,8 +223,8 @@ public class TestMigrationSubscriptions extends TestIntegrationBase {
         final LocalDate entitlementMigrationDate = new LocalDate(2015, 12, 20);
         final LocalDate billingMigrationDate = new LocalDate(2016, 2, 1);
 
-        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
-        final PlanPhaseSpecifier addOnSpec1 = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+        final PlanPhaseSpecifier addOnSpec1 = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
 
         final String externalKey = "baseExternalKey";
         EntitlementSpecifier baseEntitlementSpecifier = new DefaultEntitlementSpecifier(baseSpec, null);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
index b174219..695e480 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
@@ -198,9 +198,9 @@ public class TestSubscription extends TestIntegrationBase {
 
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
 
-        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final PlanPhaseSpecifier addOnSpec1 = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final PlanPhaseSpecifier addOnSpec2 = new PlanPhaseSpecifier("Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec1 = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec2 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final String externalKey = "baseExternalKey";
         EntitlementSpecifier baseEntitlementSpecifier = new DefaultEntitlementSpecifier(baseSpec, null);
@@ -254,7 +254,7 @@ public class TestSubscription extends TestIntegrationBase {
 
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final LocalDate futureDate = new LocalDate(2015, 10, 1);
 
@@ -286,7 +286,7 @@ public class TestSubscription extends TestIntegrationBase {
 
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final LocalDate futureDate = new LocalDate(2015, 10, 1);
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
index 4ef9834..de723e3 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
@@ -133,7 +133,7 @@ public class TestWithTimeZones extends TestIntegrationBase {
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
         busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", ProductCategory.BASE, BillingPeriod.MONTHLY, "notrial", null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", BillingPeriod.MONTHLY, "notrial", null);
         Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "Something", ImmutableList.<PlanPhasePriceOverride>of(), null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -178,7 +178,7 @@ public class TestWithTimeZones extends TestIntegrationBase {
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
         busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", ProductCategory.BASE, BillingPeriod.MONTHLY, "notrial", null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", BillingPeriod.MONTHLY, "notrial", null);
         Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "Something", ImmutableList.<PlanPhasePriceOverride>of(), null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -230,7 +230,7 @@ public class TestWithTimeZones extends TestIntegrationBase {
         }
 
         busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", ProductCategory.BASE, BillingPeriod.MONTHLY, "notrial", null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", BillingPeriod.MONTHLY, "notrial", null);
         // Pass a date of today, to trigger TimeAwareContext#toUTCDateTime
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "Something", ImmutableList.<PlanPhasePriceOverride>of(), clock.getUTCToday(), clock.getUTCToday(), false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java b/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
index 2f66d29..3bafe9c 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
@@ -19,15 +19,24 @@ package org.killbill.billing.catalog.api.user;
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.net.URI;
+import java.nio.charset.Charset;
 
 import javax.inject.Inject;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.CatalogUpdater;
 import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
+import org.killbill.billing.catalog.VersionedCatalog;
+import org.killbill.billing.catalog.api.BillingMode;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogService;
 import org.killbill.billing.catalog.api.CatalogUserApi;
+import org.killbill.billing.catalog.api.MutableStaticCatalog;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.SimplePlanDescriptor;
 import org.killbill.billing.catalog.api.StaticCatalog;
 import org.killbill.billing.catalog.caching.CatalogCache;
 import org.killbill.billing.tenant.api.TenantApiException;
@@ -37,6 +46,9 @@ import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.xmlloader.XMLLoader;
+import org.killbill.xmlloader.XMLWriter;
+
+import com.google.common.collect.ImmutableList;
 
 public class DefaultCatalogUserApi implements CatalogUserApi {
 
@@ -45,6 +57,7 @@ public class DefaultCatalogUserApi implements CatalogUserApi {
     private final TenantUserApi tenantApi;
     private final CatalogCache catalogCache;
 
+
     @Inject
     public DefaultCatalogUserApi(final CatalogService catalogService,
                                  final TenantUserApi tenantApi,
@@ -59,13 +72,13 @@ public class DefaultCatalogUserApi implements CatalogUserApi {
     @Override
     public Catalog getCatalog(final String catalogName, final TenantContext tenantContext) throws CatalogApiException {
         final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext);
-        return catalogService.getFullCatalog(internalTenantContext);
+        return catalogService.getFullCatalog(true, internalTenantContext);
     }
 
     @Override
     public StaticCatalog getCurrentCatalog(final String catalogName, final TenantContext tenantContext) throws CatalogApiException {
         final InternalTenantContext internalTenantContext = createInternalTenantContext(tenantContext);
-        return catalogService.getCurrentCatalog(internalTenantContext);
+        return catalogService.getCurrentCatalog(true, internalTenantContext);
     }
 
     @Override
@@ -85,6 +98,36 @@ public class DefaultCatalogUserApi implements CatalogUserApi {
         }
     }
 
+    @Override
+    public void addSimplePlan(final SimplePlanDescriptor descriptor, final DateTime effectiveDate, final CallContext callContext) throws CatalogApiException {
+
+        try {
+            final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(callContext);
+            final StandaloneCatalog currentCatalog = getCurrentStandaloneCatalogForTenant(internalTenantContext);
+            final CatalogUpdater catalogUpdater = (currentCatalog != null) ?
+                                                  new CatalogUpdater(currentCatalog) :
+                                                  new CatalogUpdater("dummy", BillingMode.IN_ADVANCE, effectiveDate, descriptor.getCurrency());
+
+            catalogUpdater.addSimplePlanDescriptor(descriptor);
+
+            catalogCache.clearCatalog(internalTenantContext);
+            tenantApi.updateTenantKeyValue(TenantKey.CATALOG.toString(), catalogUpdater.getCatalogXML(), callContext);
+        } catch (TenantApiException e) {
+            throw new CatalogApiException(e);
+        }
+    }
+
+
+    private StandaloneCatalog getCurrentStandaloneCatalogForTenant(final InternalTenantContext internalTenantContext) throws CatalogApiException {
+        final VersionedCatalog versionedCatalog = (VersionedCatalog) catalogService.getCurrentCatalog(false, internalTenantContext);
+        if (versionedCatalog != null && !versionedCatalog.getVersions().isEmpty()) {
+            final StandaloneCatalogWithPriceOverride standaloneCatalogWithPriceOverride = versionedCatalog.getVersions().get(versionedCatalog.getVersions().size() - 1);
+            return standaloneCatalogWithPriceOverride.getStandaloneCatalog();
+        } else {
+            return null;
+        }
+    }
+
     private InternalTenantContext createInternalTenantContext(final TenantContext tenantContext) {
         // Only tenantRecordId will be populated -- this is important to always create the (ehcache) key the same way
         return internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext);
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultSimplePlanDescriptor.java b/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultSimplePlanDescriptor.java
new file mode 100644
index 0000000..4383f53
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultSimplePlanDescriptor.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.catalog.api.user;
+
+import java.math.BigDecimal;
+
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.SimplePlanDescriptor;
+import org.killbill.billing.catalog.api.TimeUnit;
+
+public class DefaultSimplePlanDescriptor implements SimplePlanDescriptor {
+
+    private final String planId;
+    private final String productName;
+    private final Currency currency;
+    private final BigDecimal amount;
+    private final BillingPeriod billingPeriod;
+    private final int trialLength;
+    private final TimeUnit trialTimeUnit;
+
+    public DefaultSimplePlanDescriptor(final String planId,
+                                       final String productName,
+                                       final Currency currency,
+                                       final BigDecimal amount,
+                                       final BillingPeriod billingPeriod,
+                                       final int trialLength,
+                                       final TimeUnit trialTimeUnit) {
+        this.planId = planId;
+        this.productName = productName;
+        this.currency = currency;
+        this.amount = amount;
+        this.billingPeriod = billingPeriod;
+        this.trialLength = trialLength;
+        this.trialTimeUnit = trialTimeUnit;
+    }
+
+    @Override
+    public String getPlanId() {
+        return planId;
+    }
+
+    @Override
+    public String getProductName() {
+        return productName;
+    }
+
+    @Override
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    @Override
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    @Override
+    public BillingPeriod getBillingPeriod() {
+        return billingPeriod;
+    }
+
+    @Override
+    public int getTrialLength() {
+        return trialLength;
+    }
+
+    @Override
+    public TimeUnit getTrialTimeUnit() {
+        return trialTimeUnit;
+    }
+
+    @Override
+    public String toString() {
+        return "DefaultSimplePlanDescriptor{" +
+               "planId='" + planId + '\'' +
+               ", productName='" + productName + '\'' +
+               ", currency=" + currency +
+               ", amount=" + amount +
+               ", billingPeriod=" + billingPeriod +
+               ", trialLength=" + trialLength +
+               ", trialTimeUnit=" + trialTimeUnit +
+               '}';
+    }
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCache.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCache.java
index 38984c0..379fbb5 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCache.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCache.java
@@ -25,7 +25,7 @@ public interface CatalogCache {
 
     public void loadDefaultCatalog(final String url) throws CatalogApiException;
 
-    public VersionedCatalog getCatalog(InternalTenantContext tenantContext) throws CatalogApiException;
+    public VersionedCatalog getCatalog(final boolean useDefaultCatalog, InternalTenantContext tenantContext) throws CatalogApiException;
 
     public void clearCatalog(InternalTenantContext tenantContext);
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
index 5713049..047f824 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
@@ -81,7 +81,7 @@ public class EhCacheCatalogCache implements CatalogCache {
     }
 
     @Override
-    public VersionedCatalog getCatalog(final InternalTenantContext tenantContext) throws CatalogApiException {
+    public VersionedCatalog getCatalog(final boolean useDefaultCatalog, final InternalTenantContext tenantContext) throws CatalogApiException {
 
         // STEPH TODO what are the possibilities for caching here ?
         final VersionedCatalog pluginVersionedCatalog = getCatalogFromPlugins(tenantContext);
@@ -90,7 +90,7 @@ public class EhCacheCatalogCache implements CatalogCache {
         }
 
         if (tenantContext.getTenantRecordId() == InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID) {
-            return defaultCatalog;
+            return useDefaultCatalog ? defaultCatalog : null;
         }
         // The cache loader might choke on some bad xml -- unlikely since we check its validity prior storing it,
         // but to be on the safe side;;
@@ -98,7 +98,7 @@ public class EhCacheCatalogCache implements CatalogCache {
             VersionedCatalog tenantCatalog = (VersionedCatalog) cacheController.get(tenantContext.getTenantRecordId(), cacheLoaderArgument);
             // It means we are using a default catalog in a multi-tenant deployment, that does not really match a real use case, but we want to support it
             // for test purpose.
-            if (tenantCatalog == null) {
+            if (useDefaultCatalog && tenantCatalog == null) {
                 tenantCatalog = new VersionedCatalog(defaultCatalog.getClock(), defaultCatalog.getCatalogName(), defaultCatalog.getRecurringBillingMode(), defaultCatalog.getVersions(), tenantContext);
                 cacheController.add(tenantContext.getTenantRecordId(), tenantCatalog);
             }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/CatalogUpdater.java b/catalog/src/main/java/org/killbill/billing/catalog/CatalogUpdater.java
new file mode 100644
index 0000000..53c8745
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/CatalogUpdater.java
@@ -0,0 +1,299 @@
+/*
+ * 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.catalog;
+
+import java.math.BigDecimal;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.ErrorCode;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
+import org.killbill.billing.catalog.api.BillingAlignment;
+import org.killbill.billing.catalog.api.BillingMode;
+import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.PhaseType;
+import org.killbill.billing.catalog.api.PlanAlignmentChange;
+import org.killbill.billing.catalog.api.PlanAlignmentCreate;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.catalog.api.SimplePlanDescriptor;
+import org.killbill.billing.catalog.api.TimeUnit;
+import org.killbill.billing.catalog.rules.DefaultCaseBillingAlignment;
+import org.killbill.billing.catalog.rules.DefaultCaseCancelPolicy;
+import org.killbill.billing.catalog.rules.DefaultCaseChangePlanAlignment;
+import org.killbill.billing.catalog.rules.DefaultCaseChangePlanPolicy;
+import org.killbill.billing.catalog.rules.DefaultCaseCreateAlignment;
+import org.killbill.billing.catalog.rules.DefaultCasePriceList;
+import org.killbill.billing.catalog.rules.DefaultPlanRules;
+import org.killbill.xmlloader.XMLWriter;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class CatalogUpdater {
+
+    private static URI DUMMY_URI;
+    static {
+        try {
+            DUMMY_URI = new URI("dummy");
+        } catch (URISyntaxException e) {
+        }
+    };
+
+    private static final DefaultPriceList DEFAULT_PRICELIST = new DefaultPriceList().setName(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+    private final DefaultMutableStaticCatalog catalog;
+
+    public CatalogUpdater(final StandaloneCatalog standaloneCatalog) {
+        this.catalog = new DefaultMutableStaticCatalog(standaloneCatalog);
+    }
+
+    public CatalogUpdater(final String catalogName, final BillingMode billingMode, final DateTime effectiveDate, final Currency... currencies) {
+        final StandaloneCatalog tmp = new StandaloneCatalog()
+                .setCatalogName(catalogName)
+                .setEffectiveDate(effectiveDate.toDate())
+                .setSupportedCurrencies(currencies)
+                .setRecurringBillingMode(billingMode)
+                .setProducts(new DefaultProduct[0])
+                .setPlans(new DefaultPlan[0])
+                .setPriceLists(new DefaultPriceListSet(DEFAULT_PRICELIST, new DefaultPriceList[0]))
+                .setPlanRules(getSaneDefaultPlanRules());
+        tmp.initialize(tmp, DUMMY_URI);
+
+        this.catalog = new DefaultMutableStaticCatalog(tmp);
+    }
+
+    public StandaloneCatalog getCatalog() {
+        return catalog;
+    }
+
+    public String getCatalogXML() {
+        try {
+            return XMLWriter.writeXML(catalog, StandaloneCatalog.class);
+        } catch (final Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void addSimplePlanDescriptor(final SimplePlanDescriptor desc) throws CatalogApiException {
+
+        validateSimplePlanDescriptor(desc);
+
+        DefaultProduct product = getExistingProduct(desc.getProductName());
+        if (product == null) {
+            product = new DefaultProduct();
+            product.setName(desc.getProductName());
+            product.setCatagory(ProductCategory.BASE); // TODO
+            product.initialize(catalog, DUMMY_URI);
+            catalog.addProduct(product);
+        }
+
+        DefaultPlan plan = getExistingPlan(desc.getPlanId());
+        if (plan == null) {
+            plan = new DefaultPlan();
+            plan.setName(desc.getPlanId());
+            plan.setPriceListName(DEFAULT_PRICELIST.getName());
+            plan.setProduct(product);
+
+            if (desc.getTrialLength() > 0 && desc.getTrialTimeUnit() != TimeUnit.UNLIMITED) {
+                final DefaultPlanPhase trialPhase = new DefaultPlanPhase();
+                trialPhase.setPhaseType(PhaseType.TRIAL);
+                trialPhase.setDuration(new DefaultDuration().setUnit(desc.getTrialTimeUnit()).setNumber(desc.getTrialLength()));
+                trialPhase.setFixed(new DefaultFixed().setFixedPrice(new DefaultInternationalPrice().setPrices(new DefaultPrice[]{new DefaultPrice().setCurrency(desc.getCurrency()).setValue(BigDecimal.ZERO)})));
+                plan.setInitialPhases(new DefaultPlanPhase[]{trialPhase});
+            }
+            catalog.addPlan(plan);
+        } else {
+            validateExistingPlan(plan, desc);
+        }
+
+        //
+        // At this point we have an old or newly created **simple** Plan and we need to either create the recurring section or add a new currency.
+        //
+        if (!isCurrencySupported(desc.getCurrency())) {
+            catalog.addCurrency(desc.getCurrency());
+            // Reset the fixed price to null so the isZero() logic goes through new currencies and set the zero price for all
+            if (plan.getInitialPhases().length == 1) {
+                ((DefaultInternationalPrice)plan.getInitialPhases()[0].getFixed().getPrice()).setPrices(null);
+            }
+        }
+
+
+        DefaultPlanPhase evergreenPhase = plan.getFinalPhase();
+        if (evergreenPhase == null) {
+            evergreenPhase = new DefaultPlanPhase();
+            evergreenPhase.setPhaseType(PhaseType.EVERGREEN);
+            evergreenPhase.setDuration(new DefaultDuration()
+                                               .setUnit(TimeUnit.UNLIMITED));
+            plan.setFinalPhase(evergreenPhase);
+        }
+
+        DefaultRecurring recurring = (DefaultRecurring) evergreenPhase.getRecurring();
+        if (recurring == null) {
+            recurring = new DefaultRecurring();
+            recurring.setBillingPeriod(desc.getBillingPeriod());
+            recurring.setRecurringPrice(new DefaultInternationalPrice().setPrices(new DefaultPrice[0]));
+            evergreenPhase.setRecurring(recurring);
+        }
+
+        try {
+            recurring.getRecurringPrice().getPrice(desc.getCurrency());
+        } catch (CatalogApiException ignore) {
+            catalog.addRecurringPriceToPlan(recurring.getRecurringPrice(), new DefaultPrice().setCurrency(desc.getCurrency()).setValue(desc.getAmount()));
+        }
+        // Reinit catalog
+        catalog.initialize(catalog, DUMMY_URI);
+    }
+
+    private void validateExistingPlan(final DefaultPlan plan, final SimplePlanDescriptor desc) throws CatalogApiException {
+
+        boolean failedValidation = false;
+
+        //
+        // TRIAL VALIDATION
+        //
+        // We only support adding new Plan with NO TRIAL or $0 TRIAL. Existing Plan not matching such criteria are incompatible
+        if (plan.getInitialPhases().length > 1 ||
+            (plan.getInitialPhases().length == 1 &&
+             (plan.getInitialPhases()[0].getPhaseType() != PhaseType.TRIAL || !plan.getInitialPhases()[0].getFixed().getPrice().isZero()))) {
+            failedValidation = true;
+        } else {
+
+            final boolean isDescConfiguredWithTrial = desc.getTrialLength() > 0 && desc.getTrialTimeUnit() != TimeUnit.UNLIMITED;
+            final boolean isPlanConfiguredWithTrial = plan.getInitialPhases().length == 1;
+            // Current plan has trial and desc does not or reverse
+            if ((isDescConfiguredWithTrial && !isPlanConfiguredWithTrial) ||
+                (!isDescConfiguredWithTrial && isPlanConfiguredWithTrial)) {
+                failedValidation = true;
+            // Both have trials , check they match
+            } else if (isDescConfiguredWithTrial && isPlanConfiguredWithTrial) {
+                if (plan.getInitialPhases()[0].getDuration().getUnit() != desc.getTrialTimeUnit() ||
+                    plan.getInitialPhases()[0].getDuration().getNumber() != desc.getTrialLength()) {
+                    failedValidation = true;
+                }
+            }
+        }
+
+        //
+        // RECURRING VALIDATION
+        //
+        if (!failedValidation) {
+            // Desc only supports EVERGREEN Phase
+            if (plan.getFinalPhase().getPhaseType() != PhaseType.EVERGREEN) {
+                failedValidation = true;
+            } else {
+
+                // Should be same recurring BillingPeriod
+                if (plan.getFinalPhase().getRecurring().getBillingPeriod() != desc.getBillingPeriod()) {
+                    failedValidation = true;
+                } else {
+                    try {
+                        final BigDecimal currentAmount = plan.getFinalPhase().getRecurring().getRecurringPrice().getPrice(desc.getCurrency());
+                        if (currentAmount.compareTo(desc.getAmount()) != 0) {
+                            failedValidation = true;
+                        }
+                    } catch (CatalogApiException ignoreIfCurrencyIsCurrentlyUndefined) {}
+                }
+            }
+        }
+
+
+        if (failedValidation) {
+            throw new CatalogApiException(ErrorCode.CAT_FAILED_SIMPLE_PLAN_VALIDATION, plan.toString(), desc.toString());
+        }
+    }
+
+
+
+    private boolean isCurrencySupported(final Currency targetCurrency) {
+        return Iterables.any(ImmutableList.copyOf(catalog.getCurrentSupportedCurrencies()), new Predicate<Currency>() {
+            @Override
+            public boolean apply(final Currency input) {
+                return input.equals(targetCurrency);
+            }
+        });
+    }
+
+    private void validateSimplePlanDescriptor(final SimplePlanDescriptor desc) throws CatalogApiException {
+        if (desc == null ||
+            desc.getPlanId() == null ||
+            desc.getBillingPeriod() == null ||
+            desc.getProductName() == null ||
+            (desc.getAmount() == null || desc.getAmount().compareTo(BigDecimal.ZERO) <= 0) ||
+            desc.getCurrency() == null) {
+            throw new CatalogApiException(ErrorCode.CAT_INVALID_SIMPLE_PLAN_DESCRIPTOR, desc);
+        }
+    }
+
+    private DefaultProduct getExistingProduct(final String productName) {
+        return Iterables.tryFind(ImmutableList.copyOf(catalog.getCurrentProducts()), new Predicate<DefaultProduct>() {
+            @Override
+            public boolean apply(final DefaultProduct input) {
+                return input.getName().equals(productName);
+            }
+        }).orNull();
+    }
+
+    private DefaultPlan getExistingPlan(final String planName) {
+        return Iterables.tryFind(ImmutableList.copyOf(catalog.getCurrentPlans()), new Predicate<DefaultPlan>() {
+            @Override
+            public boolean apply(final DefaultPlan input) {
+                return input.getName().equals(planName);
+            }
+        }).orNull();
+    }
+
+    private DefaultPlanRules getSaneDefaultPlanRules() {
+
+        final DefaultCaseChangePlanPolicy[] changePolicy = new DefaultCaseChangePlanPolicy[1];
+        changePolicy[0] = new DefaultCaseChangePlanPolicy();
+        changePolicy[0].setPolicy(BillingActionPolicy.IMMEDIATE);
+
+        final DefaultCaseChangePlanAlignment[] changeAlignment = new DefaultCaseChangePlanAlignment[1];
+        changeAlignment[0] = new DefaultCaseChangePlanAlignment();
+        changeAlignment[0].setAlignment(PlanAlignmentChange.START_OF_BUNDLE);
+
+        final DefaultCaseCancelPolicy[] cancelPolicy = new DefaultCaseCancelPolicy[1];
+        cancelPolicy[0] = new DefaultCaseCancelPolicy();
+        cancelPolicy[0].setPolicy(BillingActionPolicy.IMMEDIATE);
+
+        final DefaultCaseCreateAlignment[] createAlignment = new DefaultCaseCreateAlignment[1];
+        createAlignment[0] = new DefaultCaseCreateAlignment();
+        createAlignment[0].setAlignment(PlanAlignmentCreate.START_OF_BUNDLE);
+
+        final DefaultCaseBillingAlignment[] billingAlignmentCase = new DefaultCaseBillingAlignment[1];
+        billingAlignmentCase[0] = new DefaultCaseBillingAlignment();
+        billingAlignmentCase[0].setAlignment(BillingAlignment.ACCOUNT);
+
+        final DefaultCasePriceList[] priceList = new DefaultCasePriceList[1];
+        priceList[0] = new DefaultCasePriceList();
+        priceList[0].setToPriceList(DEFAULT_PRICELIST);
+
+        return new DefaultPlanRules()
+                .setChangeCase(changePolicy)
+                .setChangeAlignmentCase(changeAlignment)
+                .setCancelCase(cancelPolicy)
+                .setCreateAlignmentCase(createAlignment)
+                .setBillingAlignmentCase(billingAlignmentCase)
+                .setPriceListCase(priceList);
+    }
+
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java
index cc12fa6..80895c5 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java
@@ -91,16 +91,16 @@ public class DefaultCatalogService implements KillbillService, CatalogService {
     }
 
     @Override
-    public Catalog getFullCatalog(final InternalTenantContext context) throws CatalogApiException {
-        return getCatalog(context);
+    public Catalog getFullCatalog(final boolean useDefaultCatalog, final InternalTenantContext context) throws CatalogApiException {
+        return getCatalog(useDefaultCatalog, context);
     }
 
     @Override
-    public StaticCatalog getCurrentCatalog(final InternalTenantContext context) throws CatalogApiException {
-        return getCatalog(context);
+    public StaticCatalog getCurrentCatalog(final boolean useDefaultCatalog, final InternalTenantContext context) throws CatalogApiException {
+        return getCatalog(useDefaultCatalog, context);
     }
 
-    private VersionedCatalog getCatalog(final InternalTenantContext context) throws CatalogApiException {
-        return catalogCache.getCatalog(context);
+    private VersionedCatalog getCatalog(final boolean useDefaultCatalog, final InternalTenantContext context) throws CatalogApiException {
+        return catalogCache.getCatalog(useDefaultCatalog, context);
     }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultMutableStaticCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultMutableStaticCatalog.java
new file mode 100644
index 0000000..8a5bc9a
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultMutableStaticCatalog.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.catalog;
+
+import java.lang.reflect.Array;
+import java.util.Date;
+
+import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.CatalogEntity;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.InternationalPrice;
+import org.killbill.billing.catalog.api.MutableStaticCatalog;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext;
+import org.killbill.billing.catalog.api.PlanSpecifier;
+import org.killbill.billing.catalog.api.Price;
+import org.killbill.billing.catalog.api.PriceList;
+import org.killbill.billing.catalog.api.Product;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class DefaultMutableStaticCatalog extends StandaloneCatalog implements MutableStaticCatalog {
+
+    public DefaultMutableStaticCatalog() {
+    }
+
+    public DefaultMutableStaticCatalog(final Date effectiveDate) {
+        super(effectiveDate);
+    }
+
+    public DefaultMutableStaticCatalog(final StandaloneCatalog input) {
+        this.setCatalogName(input.getCatalogName())
+            .setEffectiveDate(input.getEffectiveDate())
+            .setSupportedCurrencies(input.getCurrentSupportedCurrencies())
+            .setUnits(input.getCurrentUnits())
+            .setProducts(input.getCurrentProducts())
+            .setPlans(input.getCurrentPlans())
+            .setRecurringBillingMode(input.getRecurringBillingMode())
+            .setPlanRules(input.getPlanRules())
+            .setPriceLists(input.getPriceLists());
+        initialize(this, null);
+    }
+
+
+    @Override
+    public void addCurrency(final Currency currency) throws CatalogApiException {
+        final Currency [] newEntries = allocateNewEntries(getCurrentSupportedCurrencies(), currency);
+        setSupportedCurrencies(newEntries);
+    }
+
+    @Override
+    public void addProduct(final Product product) throws CatalogApiException {
+        final Product[] newEntries = allocateNewEntries(getCurrentProducts(), product);
+        setProducts((DefaultProduct[]) newEntries);
+    }
+
+    @Override
+    public void addPlan(final Plan plan) throws CatalogApiException {
+        final Plan[] newEntries = allocateNewEntries(getCurrentPlans(), plan);
+        setPlans((DefaultPlan[]) newEntries);
+
+        final DefaultPriceList priceList = getPriceLists().findPriceListFrom(plan.getPriceListName());
+        priceList.setPlans((DefaultPlan[])newEntries);
+    }
+
+    @Override
+    public void addPriceList(final PriceList priceList) throws CatalogApiException {
+        final PriceList[] newEntries = allocateNewEntries(getPriceLists().getChildPriceLists(), priceList);
+        final DefaultPriceListSet priceListSet = new DefaultPriceListSet((PriceListDefault) getPriceLists().getDefaultPricelist(), (DefaultPriceList[]) newEntries);
+        setPriceLists(priceListSet);
+    }
+
+
+    public void addRecurringPriceToPlan(final DefaultInternationalPrice currentPrices, final Price newPrice) throws CatalogApiException {
+        final Price [] newEntries = allocateNewEntries(currentPrices.getPrices(), newPrice);
+        currentPrices.setPrices((DefaultPrice []) newEntries);
+    }
+
+    private <T> T [] allocateNewEntries(final T [] existingEntries, final T newEntry) throws CatalogApiException  {
+
+        // Verify entry does not already exists
+        if (Iterables.any(ImmutableList.<T>copyOf(existingEntries), new Predicate<T>() {
+            @Override
+            public boolean apply(final T input) {
+                if (input instanceof CatalogEntity) {
+                    return ((CatalogEntity) input).getName().equals(((CatalogEntity) newEntry).getName());
+                } else if (input instanceof Enum) {
+                    return ((Enum) input).name().equals(((Enum) newEntry).name());
+                } else if (input instanceof Price) {
+                    return ((Price) input).getCurrency().equals(((Price) newEntry).getCurrency());
+                }
+                throw new IllegalStateException("Unexpected type " + newEntry.getClass());
+            }
+        })) {
+            //throw new CatalogApiException();
+            throw new IllegalStateException("Already existing " + newEntry);
+        }
+
+        // Realloc and assign new entry
+        final T [] newEntries = (T[]) Array.newInstance(newEntry.getClass(), existingEntries.length + 1);
+        for (int i = 0 ; i < newEntries.length + 1; i++) {
+            if (i < newEntries.length - 1) {
+                newEntries[i] = existingEntries[i];
+            } else {
+                newEntries[newEntries.length - 1] = newEntry;
+            }
+        }
+        return newEntries;
+    }
+
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
index 79503ad..885b7cb 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
@@ -65,7 +65,7 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
     private DefaultProduct product;
 
     @XmlElementWrapper(name = "initialPhases", required = false)
-    @XmlElement(name = "phase", required = true)
+    @XmlElement(name = "phase", required = false)
     private DefaultPlanPhase[] initialPhases;
 
     @XmlElement(name = "finalPhase", required = true)
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java
index 8c01b12..236cdef 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java
@@ -37,7 +37,7 @@ import org.killbill.xmlloader.ValidationErrors;
 @XmlAccessorType(XmlAccessType.NONE)
 public class DefaultPriceListSet extends ValidatingConfig<StandaloneCatalog> implements PriceListSet {
     @XmlElement(required = true, name = "defaultPriceList")
-    private PriceListDefault defaultPricelist;
+    private DefaultPriceList defaultPricelist;
 
     @XmlElement(required = false, name = "childPriceList")
     private DefaultPriceList[] childPriceLists;
@@ -48,7 +48,7 @@ public class DefaultPriceListSet extends ValidatingConfig<StandaloneCatalog> imp
         }
     }
 
-    public DefaultPriceListSet(final PriceListDefault defaultPricelist, final DefaultPriceList[] childPriceLists) {
+    public DefaultPriceListSet(final DefaultPriceList defaultPricelist, final DefaultPriceList[] childPriceLists) {
         this.defaultPricelist = defaultPricelist;
         this.childPriceLists = childPriceLists != null ? childPriceLists : new DefaultPriceList[0];
     }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java
index 9c0c1fd..91e4ffb 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java
@@ -46,16 +46,16 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
 
     @XmlElementWrapper(name = "included", required = false)
     @XmlIDREF
-    @XmlElement(name = "addonProduct", required = true)
+    @XmlElement(name = "addonProduct", required = false)
     private DefaultProduct[] included;
 
     @XmlElementWrapper(name = "available", required = false)
     @XmlIDREF
-    @XmlElement(name = "addonProduct", required = true)
+    @XmlElement(name = "addonProduct", required = false)
     private DefaultProduct[] available;
 
     @XmlElementWrapper(name = "limits", required = false)
-    @XmlElement(name = "limit", required = true)
+    @XmlElement(name = "limit", required = false)
     private DefaultLimit[] limits;
 
     //Not included in XML
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultTier.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultTier.java
index 7860f28..1baf1a3 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultTier.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultTier.java
@@ -37,11 +37,11 @@ import org.killbill.xmlloader.ValidationErrors;
 public class DefaultTier extends ValidatingConfig<StandaloneCatalog> implements Tier {
 
     @XmlElementWrapper(name = "limits", required = false)
-    @XmlElement(name = "limit", required = true)
+    @XmlElement(name = "limit", required = false)
     private DefaultLimit[] limits;
 
     @XmlElementWrapper(name = "blocks", required = false)
-    @XmlElement(name = "tieredBlock", required = true)
+    @XmlElement(name = "tieredBlock", required = false)
     private DefaultTieredBlock[] blocks;
 
     // Used to define a fixed price for the whole tier section
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultUsage.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultUsage.java
index f591cb3..1c6daa8 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultUsage.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultUsage.java
@@ -58,17 +58,17 @@ public class DefaultUsage extends ValidatingConfig<StandaloneCatalog> implements
 
     // Used for when billing usage IN_ADVANCE & CAPACITY
     @XmlElementWrapper(name = "limits", required = false)
-    @XmlElement(name = "limit", required = true)
+    @XmlElement(name = "limit", required = false)
     private DefaultLimit[] limits;
 
     // Used for when billing usage IN_ADVANCE & CONSUMABLE
     @XmlElementWrapper(name = "blocks", required = false)
-    @XmlElement(name = "block", required = true)
+    @XmlElement(name = "block", required = false)
     private DefaultBlock[] blocks;
 
     // Used for when billing usage IN_ARREAR
     @XmlElementWrapper(name = "tiers", required = false)
-    @XmlElement(name = "tier", required = true)
+    @XmlElement(name = "tier", required = false)
     private DefaultTier[] tiers;
 
     // Used to define a fixed price for the whole usage section -- bundle several limits/blocks of units.
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCase.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCase.java
index 629db2b..fe34af6 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCase.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCase.java
@@ -22,7 +22,9 @@ import org.killbill.billing.catalog.DefaultProduct;
 import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanSpecifier;
+import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.catalog.api.StaticCatalog;
 import org.killbill.xmlloader.ValidatingConfig;
@@ -49,7 +51,7 @@ public abstract class DefaultCase<T> extends ValidatingConfig<StandaloneCatalog>
 
     protected boolean satisfiesCase(final PlanSpecifier planPhase, final StaticCatalog c) throws CatalogApiException {
         return (getProduct() == null || getProduct().equals(c.findCurrentProduct(planPhase.getProductName()))) &&
-                (getProductCategory() == null || getProductCategory().equals(planPhase.getProductCategory())) &&
+               (getProductCategory() == null || getProductCategory().equals(c.findCurrentProduct(planPhase.getProductName()).getCategory())) &&
                 (getBillingPeriod() == null || getBillingPeriod().equals(planPhase.getBillingPeriod())) &&
                 (getPriceList() == null || getPriceList().equals(c.findCurrentPricelist(planPhase.getPriceListName())));
     }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java
index a9e25d9..525a5d7 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java
@@ -27,6 +27,7 @@ import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.PhaseType;
+import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceList;
@@ -73,20 +74,19 @@ public abstract class DefaultCaseChange<T> extends ValidatingConfig<StandaloneCa
 
     protected abstract T getResult();
 
-
-
     public T getResult(final PlanPhaseSpecifier from,
                        final PlanSpecifier to, final StaticCatalog catalog) throws CatalogApiException {
+
         if (
                 (phaseType == null || from.getPhaseType() == phaseType) &&
-                        (fromProduct == null || fromProduct.equals(catalog.findCurrentProduct(from.getProductName()))) &&
-                        (fromProductCategory == null || fromProductCategory.equals(from.getProductCategory())) &&
-                        (fromBillingPeriod == null || fromBillingPeriod.equals(from.getBillingPeriod())) &&
-                        (toProduct == null || toProduct.equals(catalog.findCurrentProduct(to.getProductName()))) &&
-                        (toProductCategory == null || toProductCategory.equals(to.getProductCategory())) &&
-                        (toBillingPeriod == null || toBillingPeriod.equals(to.getBillingPeriod())) &&
-                        (fromPriceList == null || fromPriceList.equals(catalog.findCurrentPricelist(from.getPriceListName()))) &&
-                        (toPriceList == null || toPriceList.equals(catalog.findCurrentPricelist(to.getPriceListName())))
+                (fromProduct == null || fromProduct.equals(catalog.findCurrentProduct(from.getProductName()))) &&
+                (fromProductCategory == null || fromProductCategory.equals(catalog.findCurrentProduct(from.getProductName()).getCategory())) &&
+                (fromBillingPeriod == null || fromBillingPeriod.equals(from.getBillingPeriod())) &&
+                (this.toProduct == null || this.toProduct.equals(catalog.findCurrentProduct(to.getProductName()))) &&
+                (toProductCategory == null || toProductCategory.equals(catalog.findCurrentProduct(to.getProductName()).getCategory())) &&
+                (toBillingPeriod == null || toBillingPeriod.equals(to.getBillingPeriod())) &&
+                (fromPriceList == null || fromPriceList.equals(catalog.findCurrentPricelist(from.getPriceListName()))) &&
+                (toPriceList == null || toPriceList.equals(catalog.findCurrentPricelist(to.getPriceListName())))
                 ) {
             return getResult();
         }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java
index b0e3222..34dd757 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java
@@ -74,7 +74,6 @@ public class DefaultPlanRules extends ValidatingConfig<StandaloneCatalog> implem
     @XmlElement(name = "priceListCase", required = false)
     private DefaultCasePriceList[] priceListCase;
 
-
     @Override
     public Iterable<CaseChangePlanPolicy> getCaseChangePlanPolicy() {
         return ImmutableList.<CaseChangePlanPolicy>copyOf(changeCase);
@@ -105,28 +104,26 @@ public class DefaultPlanRules extends ValidatingConfig<StandaloneCatalog> implem
         return ImmutableList.<CasePriceList>copyOf(priceListCase);
     }
 
-
-
     public PlanAlignmentCreate getPlanCreateAlignment(final PlanSpecifier specifier, final StaticCatalog catalog) throws CatalogApiException {
-        final PlanAlignmentCreate result =  DefaultCase.getResult(createAlignmentCase, specifier, catalog);
+        final PlanAlignmentCreate result = DefaultCase.getResult(createAlignmentCase, specifier, catalog);
         return (result != null) ? result : PlanAlignmentCreate.START_OF_BUNDLE;
     }
 
     public BillingActionPolicy getPlanCancelPolicy(final PlanPhaseSpecifier planPhase, final StaticCatalog catalog) throws CatalogApiException {
-        final BillingActionPolicy result =  DefaultCasePhase.getResult(cancelCase, planPhase, catalog);
+        final BillingActionPolicy result = DefaultCasePhase.getResult(cancelCase, planPhase, catalog);
         return (result != null) ? result : BillingActionPolicy.END_OF_TERM;
     }
 
     public BillingAlignment getBillingAlignment(final PlanPhaseSpecifier planPhase, final StaticCatalog catalog) throws CatalogApiException {
         final BillingAlignment result = DefaultCasePhase.getResult(billingAlignmentCase, planPhase, catalog);
-        return (result != null) ?  result : BillingAlignment.ACCOUNT;
+        return (result != null) ? result : BillingAlignment.ACCOUNT;
     }
 
     public PlanChangeResult planChange(final PlanPhaseSpecifier from, PlanSpecifier to, final StaticCatalog catalog) throws CatalogApiException {
         final DefaultPriceList toPriceList;
         if (to.getPriceListName() == null) { // Pricelist may be null because it is unspecified this is the principal use-case
-            toPriceList = findPriceList(from.toPlanSpecifier(), catalog);
-            to = new PlanSpecifier(to.getProductName(), to.getProductCategory(), to.getBillingPeriod(), toPriceList.getName());
+            toPriceList = findPriceList(from, catalog);
+            to = new PlanSpecifier(to.getProductName(), to.getBillingPeriod(), toPriceList.getName());
         } else {
             toPriceList = (DefaultPriceList) catalog.findCurrentPricelist(to.getPriceListName());
         }
@@ -143,7 +140,7 @@ public class DefaultPlanRules extends ValidatingConfig<StandaloneCatalog> implem
 
     public PlanAlignmentChange getPlanChangeAlignment(final PlanPhaseSpecifier from,
                                                       final PlanSpecifier to, final StaticCatalog catalog) throws CatalogApiException {
-        final PlanAlignmentChange result =  DefaultCaseChange.getResult(changeAlignmentCase, from, to, catalog);
+        final PlanAlignmentChange result = DefaultCaseChange.getResult(changeAlignmentCase, from, to, catalog);
         return (result != null) ? result : PlanAlignmentChange.START_OF_BUNDLE;
     }
 
@@ -156,7 +153,7 @@ public class DefaultPlanRules extends ValidatingConfig<StandaloneCatalog> implem
         }
         //Plan toPlan = catalog.findPlan()
 
-        final BillingActionPolicy result =  DefaultCaseChange.getResult(changeCase, from, to, catalog);
+        final BillingActionPolicy result = DefaultCaseChange.getResult(changeCase, from, to, catalog);
         return (result != null) ? result : BillingActionPolicy.END_OF_TERM;
     }
 
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 9520d62..9d2fb75 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
@@ -71,7 +71,7 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
     private Currency[] supportedCurrencies;
 
     @XmlElementWrapper(name = "units", required = false)
-    @XmlElement(name = "unit", required = true)
+    @XmlElement(name = "unit", required = false)
     private DefaultUnit[] units;
 
     @XmlElementWrapper(name = "products", required = true)
@@ -161,18 +161,18 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
       * @see org.killbill.billing.catalog.ICatalog#getPlan(java.lang.String, java.lang.String)
       */
     @Override
-    public DefaultPlan createOrFindCurrentPlan(final String productName, final BillingPeriod period, final String priceListName, final PlanPhasePriceOverridesWithCallContext unused) throws CatalogApiException {
-        if (productName == null) {
+    public DefaultPlan createOrFindCurrentPlan(final PlanSpecifier spec, final PlanPhasePriceOverridesWithCallContext unused) throws CatalogApiException {
+        if (spec.getProductName() == null) {
             throw new CatalogApiException(ErrorCode.CAT_NULL_PRODUCT_NAME);
         }
         if (priceLists == null) {
-            throw new CatalogApiException(ErrorCode.CAT_PRICE_LIST_NOT_FOUND, priceListName);
+            throw new CatalogApiException(ErrorCode.CAT_PRICE_LIST_NOT_FOUND, spec.getPriceListName());
         }
-        final Product product = findCurrentProduct(productName);
-        final DefaultPlan result = priceLists.getPlanFrom(priceListName, product, period);
+        final Product product = findCurrentProduct(spec.getProductName());
+        final DefaultPlan result = priceLists.getPlanFrom(spec.getPriceListName(), product, spec.getBillingPeriod());
         if (result == null) {
-            final String periodString = (period == null) ? "NULL" : period.toString();
-            throw new CatalogApiException(ErrorCode.CAT_PLAN_NOT_FOUND, productName, periodString, priceListName);
+            final String periodString = (spec.getBillingPeriod() == null) ? "NULL" : spec.getBillingPeriod().toString();
+            throw new CatalogApiException(ErrorCode.CAT_PLAN_NOT_FOUND, spec.getProductName(), periodString, spec.getPriceListName());
         }
         return result;
     }
@@ -344,7 +344,7 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
     @Override
     public boolean canCreatePlan(final PlanSpecifier specifier) throws CatalogApiException {
         final Product product = findCurrentProduct(specifier.getProductName());
-        final Plan plan = createOrFindCurrentPlan(specifier.getProductName(), specifier.getBillingPeriod(), specifier.getPriceListName(), null);
+        final Plan plan = createOrFindCurrentPlan(specifier, null);
         final DefaultPriceList priceList = findCurrentPriceList(specifier.getPriceListName());
 
         return (product != null) &&
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
index 1e14391..76e2c2d 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
@@ -130,8 +130,8 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
     }
 
     @Override
-    public Plan createOrFindCurrentPlan(final String productName, final BillingPeriod period, final String priceListName, final PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
-        final Plan defaultPlan = standaloneCatalog.createOrFindCurrentPlan(productName, period, priceListName, null);
+    public Plan createOrFindCurrentPlan(final PlanSpecifier spec, final PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
+        final Plan defaultPlan = standaloneCatalog.createOrFindCurrentPlan(spec, null);
 
         if (overrides == null ||
             overrides.getOverrides() == null ||
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 d70a831..cc00006 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
@@ -120,29 +120,26 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
 
     private class PlanRequestWrapper {
 
-        String name;
-        String productName;
-        BillingPeriod bp;
-        String priceListName;
-        PlanPhasePriceOverridesWithCallContext overrides;
-
-        public PlanRequestWrapper(final String name) {
-            this.name = name;
+        private final PlanSpecifier spec;
+        private final PlanPhasePriceOverridesWithCallContext overrides;
+
+        public PlanRequestWrapper(final String planName) {
+            this.spec = new PlanSpecifier(planName);
+            this.overrides = null;
         }
 
-        public PlanRequestWrapper(final String productName, final BillingPeriod bp,
-                                  final String priceListName, final PlanPhasePriceOverridesWithCallContext overrides) {
-            this.productName = productName;
-            this.bp = bp;
-            this.priceListName = priceListName;
+        public PlanRequestWrapper(final PlanSpecifier spec,
+                                  final PlanPhasePriceOverridesWithCallContext overrides) {
+            this.spec = spec;
             this.overrides = overrides;
         }
 
+
         public Plan findPlan(final StandaloneCatalogWithPriceOverride catalog) throws CatalogApiException {
-            if (name != null) {
-                return catalog.findCurrentPlan(name);
+            if (spec.getPlanName() != null) {
+                return catalog.findCurrentPlan(spec.getPlanName());
             } else {
-                return catalog.createOrFindCurrentPlan(productName, bp, priceListName, overrides);
+                return catalog.createOrFindCurrentPlan(new PlanSpecifier(spec.getProductName(), spec.getBillingPeriod(), spec.getPriceListName()), overrides);
             }
         }
     }
@@ -288,13 +285,11 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
     }
 
     @Override
-    public Plan createOrFindPlan(final String productName,
-                                 final BillingPeriod term,
-                                 final String priceListName,
+    public Plan createOrFindPlan(final PlanSpecifier spec,
                                  final PlanPhasePriceOverridesWithCallContext overrides,
                                  final DateTime requestedDate)
             throws CatalogApiException {
-        return versionForDate(requestedDate).createOrFindCurrentPlan(productName, term, priceListName, overrides);
+        return versionForDate(requestedDate).createOrFindCurrentPlan(spec, overrides);
     }
 
     @Override
@@ -307,14 +302,12 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
     }
 
     @Override
-    public Plan createOrFindPlan(final String productName,
-                                 final BillingPeriod term,
-                                 final String priceListName,
+    public Plan createOrFindPlan(final PlanSpecifier spec,
                                  final PlanPhasePriceOverridesWithCallContext overrides,
                                  final DateTime requestedDate,
                                  final DateTime subscriptionStartDate)
             throws CatalogApiException {
-        final CatalogPlanEntry entry =  findCatalogPlanEntry(new PlanRequestWrapper(productName, term, priceListName, overrides), requestedDate, subscriptionStartDate);
+        final CatalogPlanEntry entry =  findCatalogPlanEntry(new PlanRequestWrapper(spec, overrides), requestedDate, subscriptionStartDate);
         return entry.getPlan();
     }
 
@@ -463,9 +456,8 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
     }
 
     @Override
-    public Plan createOrFindCurrentPlan(final String productName, final BillingPeriod term,
-                                        final String priceList, final PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
-        return versionForDate(clock.getUTCNow()).createOrFindCurrentPlan(productName, term, priceList, overrides);
+    public Plan createOrFindCurrentPlan(final PlanSpecifier spec, final PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
+        return versionForDate(clock.getUTCNow()).createOrFindCurrentPlan(spec, overrides);
     }
 
     @Override
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java b/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
index 4e771ce..726732e 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
@@ -70,7 +70,7 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
     @Test(groups = "fast")
     public void testMissingDefaultCatalog() throws CatalogApiException {
         catalogCache.loadDefaultCatalog(null);
-        Assert.assertEquals(catalogCache.getCatalog(internalCallContext).getCatalogName(), "EmptyCatalog");
+        Assert.assertEquals(catalogCache.getCatalog(true, internalCallContext).getCatalogName(), "EmptyCatalog");
     }
 
     //
@@ -80,17 +80,17 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
     public void testDefaultCatalog() throws CatalogApiException {
         catalogCache.loadDefaultCatalog(Resources.getResource("SpyCarBasic.xml").toExternalForm());
 
-        final VersionedCatalog result = catalogCache.getCatalog(internalCallContext);
+        final VersionedCatalog result = catalogCache.getCatalog(true, internalCallContext);
         Assert.assertNotNull(result);
         final DefaultProduct[] products = result.getProducts(clock.getUTCNow());
         Assert.assertEquals(products.length, 3);
 
         // Verify the lookup with other contexts
         final VersionedCatalog resultForMultiTenantContext = new VersionedCatalog(result.getClock(), result.getCatalogName(), result.getRecurringBillingMode(), result.getVersions(), multiTenantContext);
-        Assert.assertEquals(catalogCache.getCatalog(multiTenantContext).getCatalogName(), resultForMultiTenantContext.getCatalogName());
-        Assert.assertEquals(catalogCache.getCatalog(multiTenantContext).getVersions().size(), resultForMultiTenantContext.getVersions().size());
-        for (int i = 0; i < catalogCache.getCatalog(multiTenantContext).getVersions().size(); i++) {
-            Assert.assertEquals(catalogCache.getCatalog(multiTenantContext).getVersions().get(i).getTenantRecordId(), resultForMultiTenantContext.getVersions().get(i).getTenantRecordId());
+        Assert.assertEquals(catalogCache.getCatalog(true, multiTenantContext).getCatalogName(), resultForMultiTenantContext.getCatalogName());
+        Assert.assertEquals(catalogCache.getCatalog(true, multiTenantContext).getVersions().size(), resultForMultiTenantContext.getVersions().size());
+        for (int i = 0; i < catalogCache.getCatalog(true, multiTenantContext).getVersions().size(); i++) {
+            Assert.assertEquals(catalogCache.getCatalog(true, multiTenantContext).getVersions().get(i).getTenantRecordId(), resultForMultiTenantContext.getVersions().get(i).getTenantRecordId());
         }
     }
 
@@ -131,14 +131,14 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
         });
 
         // Verify the lookup for a non-cached tenant. No system config is set yet but EhCacheCatalogCache returns a default empty one
-        VersionedCatalog differentResult = catalogCache.getCatalog(differentMultiTenantContext);
+        VersionedCatalog differentResult = catalogCache.getCatalog(true, differentMultiTenantContext);
         Assert.assertNotNull(differentResult);
         Assert.assertEquals(differentResult.getCatalogName(), "EmptyCatalog");
 
         // Make sure the cache loader isn't invoked, see https://github.com/killbill/killbill/issues/300
         shouldThrow.set(true);
 
-        differentResult = catalogCache.getCatalog(differentMultiTenantContext);
+        differentResult = catalogCache.getCatalog(true, differentMultiTenantContext);
         Assert.assertNotNull(differentResult);
         Assert.assertEquals(differentResult.getCatalogName(), "EmptyCatalog");
 
@@ -148,13 +148,13 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
         catalogCache.loadDefaultCatalog(Resources.getResource("SpyCarBasic.xml").toExternalForm());
 
         // Verify the lookup for this tenant
-        final VersionedCatalog result = catalogCache.getCatalog(multiTenantContext);
+        final VersionedCatalog result = catalogCache.getCatalog(true, multiTenantContext);
         Assert.assertNotNull(result);
         final DefaultProduct[] products = result.getProducts(clock.getUTCNow());
         Assert.assertEquals(products.length, 6);
 
         // Verify the lookup for another tenant
-        final VersionedCatalog otherResult = catalogCache.getCatalog(otherMultiTenantContext);
+        final VersionedCatalog otherResult = catalogCache.getCatalog(true, otherMultiTenantContext);
         Assert.assertNotNull(otherResult);
         final DefaultProduct[] otherProducts = otherResult.getProducts(clock.getUTCNow());
         Assert.assertEquals(otherProducts.length, 3);
@@ -162,16 +162,16 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
         shouldThrow.set(true);
 
         // Verify the lookup for this tenant
-        final VersionedCatalog result2 = catalogCache.getCatalog(multiTenantContext);
+        final VersionedCatalog result2 = catalogCache.getCatalog(true, multiTenantContext);
         Assert.assertEquals(result2, result);
 
         // Verify the lookup with another context for the same tenant
         final InternalCallContext sameMultiTenantContext = Mockito.mock(InternalCallContext.class);
         Mockito.when(sameMultiTenantContext.getAccountRecordId()).thenReturn(9102L);
         Mockito.when(sameMultiTenantContext.getTenantRecordId()).thenReturn(multiTenantRecordId);
-        Assert.assertEquals(catalogCache.getCatalog(sameMultiTenantContext), result);
+        Assert.assertEquals(catalogCache.getCatalog(true, sameMultiTenantContext), result);
 
         // Verify the lookup with the other tenant
-        Assert.assertEquals(catalogCache.getCatalog(otherMultiTenantContext), otherResult);
+        Assert.assertEquals(catalogCache.getCatalog(true, otherMultiTenantContext), otherResult);
     }
 }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/io/TestXMLWriter.java b/catalog/src/test/java/org/killbill/billing/catalog/io/TestXMLWriter.java
new file mode 100644
index 0000000..5d89b63
--- /dev/null
+++ b/catalog/src/test/java/org/killbill/billing/catalog/io/TestXMLWriter.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.catalog.io;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigDecimal;
+import java.net.URI;
+import java.nio.charset.Charset;
+
+import org.killbill.billing.catalog.CatalogTestSuiteNoDB;
+import org.killbill.billing.catalog.DefaultDuration;
+import org.killbill.billing.catalog.DefaultFixed;
+import org.killbill.billing.catalog.DefaultInternationalPrice;
+import org.killbill.billing.catalog.DefaultMutableStaticCatalog;
+import org.killbill.billing.catalog.DefaultPlan;
+import org.killbill.billing.catalog.DefaultPlanPhase;
+import org.killbill.billing.catalog.DefaultPrice;
+import org.killbill.billing.catalog.DefaultPriceListSet;
+import org.killbill.billing.catalog.DefaultProduct;
+import org.killbill.billing.catalog.DefaultRecurring;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.MutableStaticCatalog;
+import org.killbill.billing.catalog.api.PhaseType;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.catalog.api.TimeUnit;
+import org.killbill.xmlloader.XMLLoader;
+import org.killbill.xmlloader.XMLWriter;
+import org.testng.annotations.Test;
+
+import com.google.common.io.Resources;
+
+import static org.testng.Assert.assertEquals;
+
+public class TestXMLWriter extends CatalogTestSuiteNoDB {
+
+    // Verify we can marshall/unmarshall a (fairly complex catalog) catalog and get back the same result (Required to support catalog update)
+    @Test(groups = "fast")
+    public void testMarshallUnmarshall() throws Exception {
+        final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarAdvanced.xml").toExternalForm(), StandaloneCatalog.class);
+        final String oldCatalogStr = XMLWriter.writeXML(catalog, StandaloneCatalog.class);
+
+        //System.err.println(oldCatalogStr);
+
+        final StandaloneCatalog oldCatalog = XMLLoader.getObjectFromStream(new URI("dummy"), new ByteArrayInputStream(oldCatalogStr.getBytes(Charset.forName("UTF-8"))), StandaloneCatalog.class);
+        final String oldCatalogStr2 = XMLWriter.writeXML(oldCatalog, StandaloneCatalog.class);
+        assertEquals(oldCatalogStr2, oldCatalogStr);
+    }
+
+
+    @Test(groups = "fast")
+    public void testAddPlan() throws Exception {
+        final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarBasic.xml").toExternalForm(), StandaloneCatalog.class);
+
+        final MutableStaticCatalog mutableCatalog = new DefaultMutableStaticCatalog(catalog);
+
+        final DefaultProduct newProduct = new DefaultProduct();
+        newProduct.setName("Dynamic");
+        newProduct.setCatagory(ProductCategory.BASE);
+        newProduct.initialize((StandaloneCatalog) mutableCatalog, new URI("dummy"));
+
+        mutableCatalog.addProduct(newProduct);
+
+        final DefaultPlanPhase trialPhase = new DefaultPlanPhase();
+        trialPhase.setPhaseType(PhaseType.TRIAL);
+        trialPhase.setDuration(new DefaultDuration().setUnit(TimeUnit.DAYS).setNumber(14));
+        trialPhase.setFixed(new DefaultFixed().setFixedPrice(new DefaultInternationalPrice().setPrices(new DefaultPrice[]{new DefaultPrice().setCurrency(Currency.USD).setValue(BigDecimal.ZERO)})));
+
+        final DefaultPlanPhase evergreenPhase = new DefaultPlanPhase();
+        evergreenPhase.setPhaseType(PhaseType.EVERGREEN);
+        evergreenPhase.setDuration(new DefaultDuration().setUnit(TimeUnit.MONTHS).setNumber(1));
+        evergreenPhase.setRecurring(new DefaultRecurring().setBillingPeriod(BillingPeriod.MONTHLY).setRecurringPrice(new DefaultInternationalPrice().setPrices(new DefaultPrice[]{new DefaultPrice().setCurrency(Currency.USD).setValue(BigDecimal.TEN)})));
+
+        final DefaultPlan newPlan = new DefaultPlan();
+        newPlan.setName("dynamic-monthly");
+        newPlan.setPriceListName(DefaultPriceListSet.DEFAULT_PRICELIST_NAME);
+        newPlan.setProduct(newProduct);
+        newPlan.setInitialPhases(new DefaultPlanPhase[]{trialPhase});
+        newPlan.setFinalPhase(evergreenPhase);
+        // TODO Ordering breaks
+        mutableCatalog.addPlan(newPlan);
+        newPlan.initialize((StandaloneCatalog) mutableCatalog, new URI("dummy"));
+
+        final String newCatalogStr = XMLWriter.writeXML((StandaloneCatalog) mutableCatalog, StandaloneCatalog.class);
+        final StandaloneCatalog newCatalog = XMLLoader.getObjectFromStream(new URI("dummy"), new ByteArrayInputStream(newCatalogStr.getBytes(Charset.forName("UTF-8"))), StandaloneCatalog.class);
+        assertEquals(newCatalog.getCurrentPlans().length, catalog.getCurrentPlans().length + 1);
+
+        final Plan plan = newCatalog.findCurrentPlan("dynamic-monthly");
+        assertEquals(plan.getName(), "dynamic-monthly");
+        assertEquals(plan.getPriceListName(), DefaultPriceListSet.DEFAULT_PRICELIST_NAME);
+        assertEquals(plan.getProduct().getName(), "Dynamic");
+        assertEquals(plan.getProduct().getCategory(), ProductCategory.BASE);
+        assertEquals(plan.getInitialPhases().length, 1);
+        assertEquals(plan.getInitialPhases()[0].getName(), "dynamic-monthly-trial");
+        assertEquals(plan.getInitialPhases()[0].getPhaseType(), PhaseType.TRIAL);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrices().length, 1);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrices()[0].getCurrency(), Currency.USD);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrices()[0].getValue(), BigDecimal.ZERO);
+
+        assertEquals(plan.getFinalPhase().getName(), "dynamic-monthly-evergreen");
+        assertEquals(plan.getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        assertEquals(plan.getFinalPhase().getRecurring().getBillingPeriod(), BillingPeriod.MONTHLY);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices().length, 1);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getCurrency(), Currency.USD);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getValue(), BigDecimal.TEN);
+    }
+
+}
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalog.java b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalog.java
index 5e6c579..ca6adad 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalog.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalog.java
@@ -120,9 +120,9 @@ public class MockCatalog extends StandaloneCatalog implements Catalog {
     }
 
     @Override
-    public Plan createOrFindPlan(final String productName, final BillingPeriod term, final String priceListName, PlanPhasePriceOverridesWithCallContext overrides, final DateTime requestedDate)
+    public Plan createOrFindPlan(final PlanSpecifier spec, PlanPhasePriceOverridesWithCallContext overrides, final DateTime requestedDate)
             throws CatalogApiException {
-        return createOrFindCurrentPlan(productName, term, priceListName, overrides);
+        return createOrFindCurrentPlan(spec, overrides);
     }
 
     @Override
@@ -132,9 +132,9 @@ public class MockCatalog extends StandaloneCatalog implements Catalog {
     }
 
     @Override
-    public Plan createOrFindPlan(final String productName, final BillingPeriod term, final String priceListName, PlanPhasePriceOverridesWithCallContext overrides, final DateTime requestedDate,
+    public Plan createOrFindPlan(final PlanSpecifier spec, PlanPhasePriceOverridesWithCallContext overrides, final DateTime requestedDate,
                          final DateTime subscriptionStartDate) throws CatalogApiException {
-        return createOrFindCurrentPlan(productName, term, priceListName, overrides);
+        return createOrFindCurrentPlan(spec, overrides);
     }
 
     @Override
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogModule.java b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogModule.java
index f74e9de..c14322e 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogModule.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogModule.java
@@ -25,6 +25,7 @@ import org.killbill.billing.catalog.api.CatalogService;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.util.glue.KillBillModule;
 import org.mockito.Mockito;
+import org.omg.CORBA.BooleanHolder;
 
 public class MockCatalogModule extends KillBillModule {
 
@@ -38,8 +39,8 @@ public class MockCatalogModule extends KillBillModule {
 
         final CatalogService catalogService = Mockito.mock(CatalogService.class);
         try {
-            Mockito.when(catalogService.getCurrentCatalog(Mockito.any(InternalCallContext.class))).thenReturn(new MockCatalog());
-            Mockito.when(catalogService.getFullCatalog(Mockito.any(InternalCallContext.class))).thenReturn(catalog);
+            Mockito.when(catalogService.getCurrentCatalog(Mockito.any(Boolean.class), Mockito.any(InternalCallContext.class))).thenReturn(new MockCatalog());
+            Mockito.when(catalogService.getFullCatalog(Mockito.any(Boolean.class), Mockito.any(InternalCallContext.class))).thenReturn(catalog);
             bind(CatalogService.class).toInstance(catalogService);
         } catch (CatalogApiException e) {
             throw new RuntimeException(e);
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogService.java b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogService.java
index 41b66fa..5e7061b 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogService.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogService.java
@@ -40,12 +40,12 @@ public class MockCatalogService extends DefaultCatalogService {
     }
 
     @Override
-    public Catalog getFullCatalog(InternalTenantContext context) {
+    public Catalog getFullCatalog(final boolean useDefaultCatalog, InternalTenantContext context) {
         return catalog;
     }
 
     @Override
-    public StaticCatalog getCurrentCatalog(InternalTenantContext context) {
+    public StaticCatalog getCurrentCatalog(final boolean useDefaultCatalog, InternalTenantContext context) {
         return catalog;
     }
 
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCase.java b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCase.java
index b3f16dd..93774c4 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCase.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCase.java
@@ -121,7 +121,6 @@ public class TestCase extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
         assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", cat);
     }
@@ -143,7 +142,6 @@ public class TestCase extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
         assertion(Result.FOO, cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", cat);
     }
@@ -187,7 +185,6 @@ public class TestCase extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
         assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), cat);
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", cat);
     }
@@ -209,7 +206,6 @@ public class TestCase extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
         assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), cat);
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", cat);
     }
@@ -250,21 +246,21 @@ public class TestCase extends CatalogTestSuiteNoDB {
                 Result.LALA);
 
         final Result r1 = DefaultCase.getResult(new DefaultCaseResult[]{cr0, cr1, cr2, cr3},
-                                                new PlanSpecifier(product.getName(), product.getCategory(), BillingPeriod.MONTHLY, priceList.getName()), cat);
+                                                new PlanSpecifier(product.getName(), BillingPeriod.MONTHLY, priceList.getName()), cat);
         Assert.assertEquals(r1, Result.FOO);
 
         final Result r2 = DefaultCase.getResult(new DefaultCaseResult[]{cr0, cr1, cr2},
-                                                new PlanSpecifier(product.getName(), product.getCategory(), BillingPeriod.ANNUAL, priceList.getName()), cat);
+                                                new PlanSpecifier(product.getName(), BillingPeriod.ANNUAL, priceList.getName()), cat);
         Assert.assertEquals(r2, Result.DIPSY);
     }
 
     protected void assertionNull(final DefaultCaseResult cr, final String productName, final ProductCategory productCategory, final BillingPeriod bp, final String priceListName, final StandaloneCatalog cat) throws CatalogApiException {
-        Assert.assertNull(cr.getResult(new PlanSpecifier(productName, productCategory, bp, priceListName), cat));
+        Assert.assertNull(cr.getResult(new PlanSpecifier(productName, bp, priceListName), cat));
     }
 
     protected void assertionException(final DefaultCaseResult cr, final String productName, final ProductCategory productCategory, final BillingPeriod bp, final String priceListName, final StandaloneCatalog cat) {
         try {
-            cr.getResult(new PlanSpecifier(productName, productCategory, bp, priceListName), cat);
+            cr.getResult(new PlanSpecifier(productName, bp, priceListName), cat);
             Assert.fail("Expecting an exception");
         } catch (CatalogApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.CAT_PRICE_LIST_NOT_FOUND.getCode());
@@ -272,6 +268,6 @@ public class TestCase extends CatalogTestSuiteNoDB {
     }
 
     protected void assertion(final Result result, final DefaultCaseResult cr, final String productName, final ProductCategory productCategory, final BillingPeriod bp, final String priceListName, final StandaloneCatalog cat) throws CatalogApiException {
-        Assert.assertEquals(result, cr.getResult(new PlanSpecifier(productName, productCategory, bp, priceListName), cat));
+        Assert.assertEquals(result, cr.getResult(new PlanSpecifier(productName, bp, priceListName), cat));
     }
 }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCaseChange.java b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCaseChange.java
index 6de6027..92464c1 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCaseChange.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCaseChange.java
@@ -87,7 +87,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -108,20 +107,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionNull(cr,
                       product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.ANNUAL, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
@@ -136,14 +121,12 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            cat.getCurrentProducts()[1].getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            priceList1.getName(), cat.getCurrentProducts()[1].getName(),
                            PhaseType.EVERGREEN, cat);
@@ -176,31 +159,16 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertion(Result.FOO, cr,
                   cat.getCurrentProducts()[1].getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
                       product1.getName(), cat.getCurrentProducts()[1].getName(),
@@ -218,14 +186,12 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            cat.getCurrentProducts()[1].getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            priceList1.getName(), cat.getCurrentProducts()[1].getName(),
                            PhaseType.EVERGREEN, cat);
@@ -258,7 +224,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -270,23 +235,9 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
 
         assertion(Result.FOO, cr,
                   product1.getName(), cat.getCurrentProducts()[1].getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -307,14 +258,12 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            cat.getCurrentProducts()[1].getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            priceList1.getName(), cat.getCurrentProducts()[1].getName(),
                            PhaseType.EVERGREEN, cat);
@@ -347,7 +296,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -368,20 +316,12 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.ADD_ON, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
                       product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.ANNUAL, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
@@ -396,14 +336,12 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            cat.getCurrentProducts()[1].getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            priceList1.getName(), cat.getCurrentProducts()[1].getName(),
                            PhaseType.EVERGREEN, cat);
@@ -436,7 +374,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -455,16 +392,9 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.ADD_ON,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -485,14 +415,12 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            cat.getCurrentProducts()[1].getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            priceList1.getName(), cat.getCurrentProducts()[1].getName(),
                            PhaseType.EVERGREEN, cat);
@@ -525,7 +453,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -538,20 +465,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
                       PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
                       product1.getName(), cat.getCurrentProducts()[1].getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
@@ -560,7 +473,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.ANNUAL, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -574,14 +486,12 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            cat.getCurrentProducts()[1].getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            priceList1.getName(), cat.getCurrentProducts()[1].getName(),
                            PhaseType.EVERGREEN, cat);
@@ -614,7 +524,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -626,19 +535,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
                       product1.getName(), cat.getCurrentProducts()[1].getName(),
@@ -656,21 +552,18 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.ANNUAL,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            cat.getCurrentProducts()[1].getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            priceList1.getName(), cat.getCurrentProducts()[1].getName(),
                            PhaseType.EVERGREEN, cat);
@@ -703,7 +596,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -715,19 +607,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
                       product1.getName(), cat.getCurrentProducts()[1].getName(),
@@ -752,14 +631,12 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   cat.getCurrentProducts()[1].getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            priceList1.getName(), cat.getCurrentProducts()[1].getName(),
                            PhaseType.EVERGREEN, cat);
@@ -792,7 +669,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -804,19 +680,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
                       product1.getName(), cat.getCurrentProducts()[1].getName(),
@@ -841,14 +704,12 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            cat.getCurrentProducts()[1].getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), cat.getCurrentProducts()[1].getName(),
                   PhaseType.EVERGREEN, cat);
@@ -881,7 +742,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -893,19 +753,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
                       product1.getName(), cat.getCurrentProducts()[1].getName(),
@@ -930,21 +777,18 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            cat.getCurrentProducts()[1].getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                            priceList1.getName(), cat.getCurrentProducts()[1].getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.TRIAL, cat);
@@ -1001,14 +845,14 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
                 Result.LALA);
 
         final Result r1 = DefaultCaseChange.getResult(new DefaultCaseChangeResult[]{cr0, cr1, cr2, cr3, cr4},
-                                                      new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN),
-                                                      new PlanSpecifier(product2.getName(), product2.getCategory(), BillingPeriod.MONTHLY, priceList2.getName()), cat);
+                                                      new PlanPhaseSpecifier(product1.getName(),  BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN),
+                                                      new PlanSpecifier(product2.getName(),  BillingPeriod.MONTHLY, priceList2.getName()), cat);
 
         Assert.assertEquals(r1, Result.FOO);
 
         final Result r2 = DefaultCaseChange.getResult(new DefaultCaseChangeResult[]{cr0, cr1, cr2, cr3, cr4},
-                                                      new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN),
-                                                      new PlanSpecifier(product2.getName(), product2.getCategory(), BillingPeriod.ANNUAL, priceList2.getName()), cat);
+                                                      new PlanPhaseSpecifier(product1.getName(),  BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN),
+                                                      new PlanSpecifier(product2.getName(),  BillingPeriod.ANNUAL, priceList2.getName()), cat);
 
         Assert.assertEquals(r2, Result.DIPSY);
     }
@@ -1020,8 +864,8 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
                                  final String fromPriceListName, final String toPriceListName,
                                  final PhaseType phaseType, final StandaloneCatalog cat) {
         try {
-            Assert.assertNull(cr.getResult(new PlanPhaseSpecifier(fromProductName, fromProductCategory, fromBp, fromPriceListName, phaseType),
-                                           new PlanSpecifier(toProductName, toProductCategory, toBp, toPriceListName), cat));
+            Assert.assertNull(cr.getResult(new PlanPhaseSpecifier(fromProductName, fromBp, fromPriceListName, phaseType),
+                                           new PlanSpecifier(toProductName, toBp, toPriceListName), cat));
         } catch (CatalogApiException e) {
             Assert.fail("", e);
         }
@@ -1029,13 +873,12 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
     protected void assertionException(final DefaultCaseChangeResult cr,
                                       final String fromProductName, final String toProductName,
-                                      final ProductCategory fromProductCategory, final ProductCategory toProductCategory,
                                       final BillingPeriod fromBp, final BillingPeriod toBp,
                                       final String fromPriceListName, final String toPriceListName,
                                       final PhaseType phaseType, final StandaloneCatalog cat) {
         try {
-            cr.getResult(new PlanPhaseSpecifier(fromProductName, fromProductCategory, fromBp, fromPriceListName, phaseType),
-                         new PlanSpecifier(toProductName, toProductCategory, toBp, toPriceListName), cat);
+            cr.getResult(new PlanPhaseSpecifier(fromProductName, fromBp, fromPriceListName, phaseType),
+                         new PlanSpecifier(toProductName, toBp, toPriceListName), cat);
             Assert.fail("Expecting an exception");
         } catch (CatalogApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.CAT_PRICE_LIST_NOT_FOUND.getCode());
@@ -1044,13 +887,12 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
     protected void assertion(final Result result, final DefaultCaseChangeResult cr,
                              final String fromProductName, final String toProductName,
-                             final ProductCategory fromProductCategory, final ProductCategory toProductCategory,
                              final BillingPeriod fromBp, final BillingPeriod toBp,
                              final String fromPriceListName, final String toPriceListName,
                              final PhaseType phaseType, final StandaloneCatalog cat) {
         try {
-            Assert.assertEquals(result, cr.getResult(new PlanPhaseSpecifier(fromProductName, fromProductCategory, fromBp, fromPriceListName, phaseType),
-                                                     new PlanSpecifier(toProductName, toProductCategory, toBp, toPriceListName), cat));
+            Assert.assertEquals(result, cr.getResult(new PlanPhaseSpecifier(fromProductName, fromBp, fromPriceListName, phaseType),
+                                                     new PlanSpecifier(toProductName, toBp, toPriceListName), cat));
         } catch (CatalogApiException e) {
             Assert.fail("", e);
         }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCasePhase.java b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCasePhase.java
index 3fa6052..22ecc52 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCasePhase.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCasePhase.java
@@ -74,7 +74,6 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
@@ -97,7 +96,6 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertion(Result.FOO, cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
@@ -143,7 +141,6 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
@@ -166,7 +163,6 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
@@ -189,7 +185,6 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
@@ -243,12 +238,12 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
                 Result.LALA);
 
         final Result r1 = DefaultCasePhase.getResult(new DefaultCaseResult[]{cr0, cr1, cr2, cr3, cr4},
-                                                     new PlanPhaseSpecifier(product.getName(), product.getCategory(), BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN), cat);
+                                                     new PlanPhaseSpecifier(product.getName(), BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN), cat);
 
         Assert.assertEquals(Result.FOO, r1);
 
         final Result r2 = DefaultCasePhase.getResult(new DefaultCaseResult[]{cr0, cr1, cr2, cr3, cr4},
-                                                     new PlanPhaseSpecifier(product.getName(), product.getCategory(), BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN), cat);
+                                                     new PlanPhaseSpecifier(product.getName(), BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN), cat);
 
         Assert.assertEquals(Result.DIPSY, r2);
 
@@ -256,7 +251,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
 
     protected void assertionNull(final DefaultCaseResult cr, final String productName, final ProductCategory productCategory, final BillingPeriod bp, final String priceListName, final PhaseType phaseType, final StandaloneCatalog cat) {
         try {
-            Assert.assertNull(cr.getResult(new PlanPhaseSpecifier(productName, productCategory, bp, priceListName, phaseType), cat));
+            Assert.assertNull(cr.getResult(new PlanPhaseSpecifier(productName, bp, priceListName, phaseType), cat));
         } catch (CatalogApiException e) {
             Assert.fail("", e);
         }
@@ -264,7 +259,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
 
     protected void assertionException(final DefaultCaseResult cr, final String productName, final ProductCategory productCategory, final BillingPeriod bp, final String priceListName, final PhaseType phaseType, final StandaloneCatalog cat) {
         try {
-            Assert.assertNull(cr.getResult(new PlanPhaseSpecifier(productName, productCategory, bp, priceListName, phaseType), cat));
+            Assert.assertNull(cr.getResult(new PlanPhaseSpecifier(productName, bp, priceListName, phaseType), cat));
             Assert.fail("Exception expected");
         } catch (CatalogApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.CAT_PRICE_LIST_NOT_FOUND.getCode());
@@ -273,7 +268,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
 
     protected void assertion(final Result result, final DefaultCaseResult cr, final String productName, final ProductCategory productCategory, final BillingPeriod bp, final String priceListName, final PhaseType phaseType, final StandaloneCatalog cat) {
         try {
-            Assert.assertEquals(result, cr.getResult(new PlanPhaseSpecifier(productName, productCategory, bp, priceListName, phaseType), cat));
+            Assert.assertEquals(result, cr.getResult(new PlanPhaseSpecifier(productName, bp, priceListName, phaseType), cat));
         } catch (CatalogApiException e) {
             Assert.fail("", e);
         }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestLoadRules.java b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestLoadRules.java
index 47ca215..fc7b061 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestLoadRules.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestLoadRules.java
@@ -40,13 +40,13 @@ public class TestLoadRules extends CatalogTestSuiteNoDB {
         Assert.assertNotNull(catalog);
         final DefaultPlanRules rules = catalog.getPlanRules();
 
-        final PlanSpecifier specifier = new PlanSpecifier("Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY,
+        final PlanSpecifier specifier = new PlanSpecifier("Laser-Scope", BillingPeriod.MONTHLY,
                                                           "DEFAULT");
 
         final PlanAlignmentCreate alignment = rules.getPlanCreateAlignment(specifier, catalog);
         Assert.assertEquals(alignment, PlanAlignmentCreate.START_OF_SUBSCRIPTION);
 
-        final PlanSpecifier specifier2 = new PlanSpecifier("Extra-Ammo", ProductCategory.ADD_ON, BillingPeriod.MONTHLY,
+        final PlanSpecifier specifier2 = new PlanSpecifier("Extra-Ammo", BillingPeriod.MONTHLY,
                                                            "DEFAULT");
 
         final PlanAlignmentCreate alignment2 = rules.getPlanCreateAlignment(specifier2, catalog);
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestPlanRules.java b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestPlanRules.java
index 152ad5f..d5acda6 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestPlanRules.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestPlanRules.java
@@ -60,8 +60,8 @@ public class TestPlanRules extends CatalogTestSuiteNoDB {
         final DefaultProduct product1 = cat.getCurrentProducts()[0];
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        final PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
-        final PlanSpecifier to = new PlanSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName());
+        final PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
+        final PlanSpecifier to = new PlanSpecifier(product1.getName(), BillingPeriod.MONTHLY, priceList1.getName());
 
         try {
             cat.getPlanRules().planChange(from, to, cat);
@@ -78,8 +78,8 @@ public class TestPlanRules extends CatalogTestSuiteNoDB {
         final DefaultProduct product1 = cat.getCurrentProducts()[0];
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        final PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
-        final PlanSpecifier to = new PlanSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.ANNUAL, priceList1.getName());
+        final PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
+        final PlanSpecifier to = new PlanSpecifier(product1.getName(), BillingPeriod.ANNUAL, priceList1.getName());
 
         PlanChangeResult result = null;
         try {
@@ -102,8 +102,8 @@ public class TestPlanRules extends CatalogTestSuiteNoDB {
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
         final DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[0];
 
-        final PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
-        final PlanSpecifier to = new PlanSpecifier(product2.getName(), product2.getCategory(), BillingPeriod.MONTHLY, null);
+        final PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
+        final PlanSpecifier to = new PlanSpecifier(product2.getName(), BillingPeriod.MONTHLY, null);
 
         PlanChangeResult result = null;
         try {
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogService.java b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogService.java
index 2557916..a7365e0 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogService.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogService.java
@@ -36,8 +36,8 @@ public class TestCatalogService extends CatalogTestSuiteNoDB {
 
         }, tenantInternalApi, catalogCache, cacheInvalidationCallback);
         service.loadCatalog();
-        Assert.assertNotNull(service.getFullCatalog(internalCallContext));
-        Assert.assertEquals(service.getFullCatalog(internalCallContext).getCatalogName(), "WeaponsHireSmall");
+        Assert.assertNotNull(service.getFullCatalog(true, internalCallContext));
+        Assert.assertEquals(service.getFullCatalog(true, internalCallContext).getCatalogName(), "WeaponsHireSmall");
     }
 
     @Test(groups = "fast")
@@ -50,7 +50,7 @@ public class TestCatalogService extends CatalogTestSuiteNoDB {
 
         },  tenantInternalApi, catalogCache, cacheInvalidationCallback);
         service.loadCatalog();
-        Assert.assertNotNull(service.getFullCatalog(internalCallContext));
-        Assert.assertEquals(service.getFullCatalog(internalCallContext).getCatalogName(), "Firearms");
+        Assert.assertNotNull(service.getFullCatalog(true, internalCallContext));
+        Assert.assertEquals(service.getFullCatalog(true, internalCallContext).getCatalogName(), "Firearms");
     }
 }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogUpdater.java b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogUpdater.java
new file mode 100644
index 0000000..eab9fa3
--- /dev/null
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogUpdater.java
@@ -0,0 +1,587 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.catalog;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigDecimal;
+import java.net.URI;
+import java.nio.charset.Charset;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.ErrorCode;
+import org.killbill.billing.catalog.api.BillingMode;
+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.MutableStaticCatalog;
+import org.killbill.billing.catalog.api.PhaseType;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PriceList;
+import org.killbill.billing.catalog.api.Product;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.catalog.api.SimplePlanDescriptor;
+import org.killbill.billing.catalog.api.TimeUnit;
+import org.killbill.billing.catalog.api.user.DefaultSimplePlanDescriptor;
+import org.killbill.xmlloader.XMLLoader;
+import org.killbill.xmlloader.XMLWriter;
+import org.testng.annotations.Test;
+
+import com.google.common.io.Resources;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.fail;
+
+public class TestCatalogUpdater extends CatalogTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testAddNoTrialPlanOnFirstCatalog() throws CatalogApiException {
+
+        final DateTime now = clock.getUTCNow();
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("foo-monthly", "Foo", Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED);
+
+        final CatalogUpdater catalogUpdater = new CatalogUpdater("dummy", BillingMode.IN_ADVANCE, now, desc.getCurrency());
+
+        catalogUpdater.addSimplePlanDescriptor(desc);
+
+        final StandaloneCatalog catalog = catalogUpdater.getCatalog();
+
+        assertEquals(catalog.getCurrentProducts().length, 1);
+
+        final Product product = catalog.getCurrentProducts()[0];
+        assertEquals(product.getName(), "Foo");
+        assertEquals(product.getCategory(), ProductCategory.BASE);
+
+        assertEquals(catalog.getCurrentPlans().length, 1);
+
+        final Plan plan = catalog.findCurrentPlan("foo-monthly");
+        assertEquals(plan.getName(), "foo-monthly");
+
+        assertEquals(plan.getInitialPhases().length, 0);
+        assertEquals(plan.getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        assertNull(plan.getFinalPhase().getFixed());
+        assertEquals(plan.getFinalPhase().getName(), "foo-monthly-evergreen");
+
+        assertEquals(plan.getFinalPhase().getRecurring().getBillingPeriod(), BillingPeriod.MONTHLY);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices().length, 1);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getValue(), BigDecimal.TEN);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getCurrency(), Currency.EUR);
+
+        assertEquals(catalog.getPriceLists().getAllPriceLists().size(), 1);
+        final PriceList priceList = catalog.getPriceLists().getAllPriceLists().get(0);
+        assertEquals(priceList.getName(), new PriceListDefault().getName());
+        assertEquals(priceList.getPlans().length, 1);
+        assertEquals(priceList.getPlans()[0].getName(), "foo-monthly");
+    }
+
+
+    @Test(groups = "fast")
+    public void testAddTrialPlanOnFirstCatalog() throws CatalogApiException {
+
+        final DateTime now = clock.getUTCNow();
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("foo-monthly", "Foo", Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 14, TimeUnit.DAYS);
+
+        final CatalogUpdater catalogUpdater = new CatalogUpdater("dummy", BillingMode.IN_ADVANCE, now, desc.getCurrency());
+
+        catalogUpdater.addSimplePlanDescriptor(desc);
+
+        final StandaloneCatalog catalog = catalogUpdater.getCatalog();
+
+        assertEquals(catalog.getCurrentProducts().length, 1);
+
+        final Product product = catalog.getCurrentProducts()[0];
+        assertEquals(product.getName(), "Foo");
+        assertEquals(product.getCategory(), ProductCategory.BASE);
+
+        assertEquals(catalog.getCurrentPlans().length, 1);
+
+        final Plan plan = catalog.findCurrentPlan("foo-monthly");
+        assertEquals(plan.getName(), "foo-monthly");
+
+        assertEquals(plan.getInitialPhases().length, 1);
+        assertEquals(plan.getInitialPhases()[0].getPhaseType(), PhaseType.TRIAL);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrices().length, 1);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrices()[0].getCurrency(), Currency.EUR);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrices()[0].getValue(), BigDecimal.ZERO);
+        assertEquals(plan.getInitialPhases()[0].getName(), "foo-monthly-trial");
+
+        assertEquals(plan.getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        assertNull(plan.getFinalPhase().getFixed());
+        assertEquals(plan.getFinalPhase().getName(), "foo-monthly-evergreen");
+
+        assertEquals(plan.getFinalPhase().getRecurring().getBillingPeriod(), BillingPeriod.MONTHLY);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices().length, 1);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getValue(), BigDecimal.TEN);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getCurrency(), Currency.EUR);
+
+        assertEquals(catalog.getPriceLists().getAllPriceLists().size(), 1);
+        final PriceList priceList = catalog.getPriceLists().getAllPriceLists().get(0);
+        assertEquals(priceList.getName(), new PriceListDefault().getName());
+        assertEquals(priceList.getPlans().length, 1);
+        assertEquals(priceList.getPlans()[0].getName(), "foo-monthly");
+    }
+
+
+
+    @Test(groups = "fast")
+    public void testAddPlanOnExistingCatalog() throws Exception {
+
+        final StandaloneCatalog originalCatalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarBasic.xml").toExternalForm(), StandaloneCatalog.class);
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().size(), 1);
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getName(), new PriceListDefault().getName());
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getPlans().length, 3);
+
+        final CatalogUpdater catalogUpdater = new CatalogUpdater(originalCatalog);
+
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("standard-annual", "Standard", Currency.USD, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED);
+        catalogUpdater.addSimplePlanDescriptor(desc);
+
+        final StandaloneCatalog catalog = catalogUpdater.getCatalog();
+
+        final Plan plan = catalog.findCurrentPlan("standard-annual");
+        assertEquals(plan.getName(), "standard-annual");
+
+        assertEquals(plan.getInitialPhases().length, 0);
+        assertEquals(plan.getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        assertNull(plan.getFinalPhase().getFixed());
+        assertEquals(plan.getFinalPhase().getName(), "standard-annual-evergreen");
+
+        assertEquals(plan.getFinalPhase().getRecurring().getBillingPeriod(), BillingPeriod.MONTHLY);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices().length, 1);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getValue(), BigDecimal.TEN);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getCurrency(), Currency.USD);
+
+        assertEquals(catalog.getPriceLists().getAllPriceLists().size(), 1);
+        final PriceList priceList = catalog.getPriceLists().getAllPriceLists().get(0);
+        assertEquals(priceList.getName(), new PriceListDefault().getName());
+        assertEquals(priceList.getPlans().length, 4);
+    }
+
+
+
+    @Test(groups = "fast")
+    public void testAddExistingPlanWithNewCurrency() throws Exception {
+        final StandaloneCatalog originalCatalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarBasic.xml").toExternalForm(), StandaloneCatalog.class);
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().size(), 1);
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getName(), new PriceListDefault().getName());
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getPlans().length, 3);
+
+        final CatalogUpdater catalogUpdater = new CatalogUpdater(originalCatalog);
+
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.DAYS);
+        catalogUpdater.addSimplePlanDescriptor(desc);
+
+        final StandaloneCatalog catalog = catalogUpdater.getCatalog();
+
+        final Plan plan = catalog.findCurrentPlan("standard-monthly");
+        assertEquals(plan.getName(), "standard-monthly");
+
+        assertEquals(plan.getInitialPhases().length, 1);
+        assertEquals(plan.getInitialPhases()[0].getPhaseType(), PhaseType.TRIAL);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrices().length, 3);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrice(Currency.EUR), BigDecimal.ZERO);
+        assertEquals(plan.getInitialPhases()[0].getName(), "standard-monthly-trial");
+
+        assertEquals(plan.getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        assertNull(plan.getFinalPhase().getFixed());
+        assertEquals(plan.getFinalPhase().getName(), "standard-monthly-evergreen");
+        assertEquals(plan.getFinalPhase().getRecurring().getBillingPeriod(), BillingPeriod.MONTHLY);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices().length, 3);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrice(Currency.EUR), BigDecimal.TEN);
+    }
+
+    @Test(groups = "fast")
+    public void testInvalidPlanDescriptors() throws Exception {
+        final StandaloneCatalog originalCatalog = enhanceOriginalCatalogForInvalidTestCases("SpyCarBasic.xml");
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().size(), 1);
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getName(), new PriceListDefault().getName());
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getPlans().length, 5);
+
+        CatalogUpdater catalogUpdater = new CatalogUpdater(originalCatalog);
+
+        // Existing Plan has a 30 days trial => try with no TRIAL
+        SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, null);
+        addBadSimplePlanDescriptor(catalogUpdater, desc);
+
+
+        // Existing Plan has a 30 days trial => try different trial length
+        desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 14, TimeUnit.DAYS);
+        addBadSimplePlanDescriptor(catalogUpdater, desc);
+
+        // Existing Plan has a 30 days trial => try different trial unit
+        desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.MONTHS);
+        addBadSimplePlanDescriptor(catalogUpdater, desc);
+
+        // Existing Plan has a MONTHLY recurring => try with ANNUAL BillingPeriod
+        desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", Currency.EUR, BigDecimal.TEN, BillingPeriod.ANNUAL, 30, TimeUnit.DAYS);
+        addBadSimplePlanDescriptor(catalogUpdater, desc);
+
+        // Existing Plan has a discount phase
+        desc = new DefaultSimplePlanDescriptor("dynamic-monthly", "Dynamic", Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.MONTHS);
+        addBadSimplePlanDescriptor(catalogUpdater, desc);
+
+        // Existing Plan has final fixedterm phase
+        desc = new DefaultSimplePlanDescriptor("superdynamic-fixedterm", "SuperDynamic", Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.DAYS);
+        addBadSimplePlanDescriptor(catalogUpdater, desc);
+
+        // Existing Plan a different recurring price ($100)
+        desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", Currency.USD, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.DAYS);
+        addBadSimplePlanDescriptor(catalogUpdater, desc);
+    }
+
+
+    @Test(groups = "fast")
+    public void testVerifyXML() throws Exception {
+
+        final StandaloneCatalog originalCatalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarBasic.xml").toExternalForm(), StandaloneCatalog.class);
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().size(), 1);
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getName(), new PriceListDefault().getName());
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getPlans().length, 3);
+
+        final CatalogUpdater catalogUpdater = new CatalogUpdater(originalCatalog);
+
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("dynamic-annual", "Dynamic", Currency.USD, BigDecimal.TEN, BillingPeriod.MONTHLY, 14, TimeUnit.DAYS);
+        catalogUpdater.addSimplePlanDescriptor(desc);
+
+        final String expectedXML = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
+                                   "<catalog>\n" +
+                                   "    <effectiveDate>2013-02-08T00:00:00Z</effectiveDate>\n" +
+                                   "    <catalogName>SpyCarBasic</catalogName>\n" +
+                                   "    <recurringBillingMode>IN_ADVANCE</recurringBillingMode>\n" +
+                                   "    <currencies>\n" +
+                                   "        <currency>USD</currency>\n" +
+                                   "        <currency>GBP</currency>\n" +
+                                   "    </currencies>\n" +
+                                   "    <products>\n" +
+                                   "        <product name=\"Standard\">\n" +
+                                   "            <category>BASE</category>\n" +
+                                   "            <included/>\n" +
+                                   "            <available/>\n" +
+                                   "            <limits/>\n" +
+                                   "        </product>\n" +
+                                   "        <product name=\"Sports\">\n" +
+                                   "            <category>BASE</category>\n" +
+                                   "            <included/>\n" +
+                                   "            <available/>\n" +
+                                   "            <limits/>\n" +
+                                   "        </product>\n" +
+                                   "        <product name=\"Super\">\n" +
+                                   "            <category>BASE</category>\n" +
+                                   "            <included/>\n" +
+                                   "            <available/>\n" +
+                                   "            <limits/>\n" +
+                                   "        </product>\n" +
+                                   "        <product name=\"Dynamic\">\n" +
+                                   "            <category>BASE</category>\n" +
+                                   "            <included/>\n" +
+                                   "            <available/>\n" +
+                                   "            <limits/>\n" +
+                                   "        </product>\n" +
+                                   "    </products>\n" +
+                                   "    <rules>\n" +
+                                   "        <changePolicy>\n" +
+                                   "            <changePolicyCase>\n" +
+                                   "                <policy>IMMEDIATE</policy>\n" +
+                                   "            </changePolicyCase>\n" +
+                                   "        </changePolicy>\n" +
+                                   "        <changeAlignment>\n" +
+                                   "            <changeAlignmentCase>\n" +
+                                   "                <alignment>START_OF_BUNDLE</alignment>\n" +
+                                   "            </changeAlignmentCase>\n" +
+                                   "        </changeAlignment>\n" +
+                                   "        <cancelPolicy>\n" +
+                                   "            <cancelPolicyCase>\n" +
+                                   "                <policy>IMMEDIATE</policy>\n" +
+                                   "            </cancelPolicyCase>\n" +
+                                   "        </cancelPolicy>\n" +
+                                   "        <createAlignment>\n" +
+                                   "            <createAlignmentCase>\n" +
+                                   "                <alignment>START_OF_BUNDLE</alignment>\n" +
+                                   "            </createAlignmentCase>\n" +
+                                   "        </createAlignment>\n" +
+                                   "        <billingAlignment>\n" +
+                                   "            <billingAlignmentCase>\n" +
+                                   "                <alignment>ACCOUNT</alignment>\n" +
+                                   "            </billingAlignmentCase>\n" +
+                                   "        </billingAlignment>\n" +
+                                   "        <priceList>\n" +
+                                   "            <priceListCase>\n" +
+                                   "                <toPriceList>DEFAULT</toPriceList>\n" +
+                                   "            </priceListCase>\n" +
+                                   "        </priceList>\n" +
+                                   "    </rules>\n" +
+                                   "    <plans>\n" +
+                                   "        <plan name=\"standard-monthly\">\n" +
+                                   "            <product>Standard</product>\n" +
+                                   "            <initialPhases>\n" +
+                                   "                <phase type=\"TRIAL\">\n" +
+                                   "                    <duration>\n" +
+                                   "                        <unit>DAYS</unit>\n" +
+                                   "                        <number>30</number>\n" +
+                                   "                    </duration>\n" +
+                                   "                    <fixed type=\"ONE_TIME\">\n" +
+                                   "                        <fixedPrice>\n" +
+                                   "                            <price>\n" +
+                                   "<currency>USD</currency>\n" +
+                                   "<value>0</value>\n" +
+                                   "                            </price>\n" +
+                                   "                            <price>\n" +
+                                   "<currency>GBP</currency>\n" +
+                                   "<value>0</value>\n" +
+                                   "                            </price>\n" +
+                                   "                        </fixedPrice>\n" +
+                                   "                    </fixed>\n" +
+                                   "                    <usages/>\n" +
+                                   "                </phase>\n" +
+                                   "            </initialPhases>\n" +
+                                   "            <finalPhase type=\"EVERGREEN\">\n" +
+                                   "                <duration>\n" +
+                                   "                    <unit>UNLIMITED</unit>\n" +
+                                   "                    <number>-1</number>\n" +
+                                   "                </duration>\n" +
+                                   "                <recurring>\n" +
+                                   "                    <billingPeriod>MONTHLY</billingPeriod>\n" +
+                                   "                    <recurringPrice>\n" +
+                                   "                        <price>\n" +
+                                   "                            <currency>GBP</currency>\n" +
+                                   "                            <value>75.00</value>\n" +
+                                   "                        </price>\n" +
+                                   "                        <price>\n" +
+                                   "                            <currency>USD</currency>\n" +
+                                   "                            <value>100.00</value>\n" +
+                                   "                        </price>\n" +
+                                   "                    </recurringPrice>\n" +
+                                   "                </recurring>\n" +
+                                   "                <usages/>\n" +
+                                   "            </finalPhase>\n" +
+                                   "            <plansAllowedInBundle>1</plansAllowedInBundle>\n" +
+                                   "        </plan>\n" +
+                                   "        <plan name=\"sports-monthly\">\n" +
+                                   "            <product>Sports</product>\n" +
+                                   "            <initialPhases>\n" +
+                                   "                <phase type=\"TRIAL\">\n" +
+                                   "                    <duration>\n" +
+                                   "                        <unit>DAYS</unit>\n" +
+                                   "                        <number>30</number>\n" +
+                                   "                    </duration>\n" +
+                                   "                    <fixed type=\"ONE_TIME\">\n" +
+                                   "                        <fixedPrice>\n" +
+                                   "                            <price>\n" +
+                                   "<currency>USD</currency>\n" +
+                                   "<value>0</value>\n" +
+                                   "                            </price>\n" +
+                                   "                            <price>\n" +
+                                   "<currency>GBP</currency>\n" +
+                                   "<value>0</value>\n" +
+                                   "                            </price>\n" +
+                                   "                        </fixedPrice>\n" +
+                                   "                    </fixed>\n" +
+                                   "                    <usages/>\n" +
+                                   "                </phase>\n" +
+                                   "            </initialPhases>\n" +
+                                   "            <finalPhase type=\"EVERGREEN\">\n" +
+                                   "                <duration>\n" +
+                                   "                    <unit>UNLIMITED</unit>\n" +
+                                   "                    <number>-1</number>\n" +
+                                   "                </duration>\n" +
+                                   "                <recurring>\n" +
+                                   "                    <billingPeriod>MONTHLY</billingPeriod>\n" +
+                                   "                    <recurringPrice>\n" +
+                                   "                        <price>\n" +
+                                   "                            <currency>GBP</currency>\n" +
+                                   "                            <value>375.00</value>\n" +
+                                   "                        </price>\n" +
+                                   "                        <price>\n" +
+                                   "                            <currency>USD</currency>\n" +
+                                   "                            <value>500.00</value>\n" +
+                                   "                        </price>\n" +
+                                   "                    </recurringPrice>\n" +
+                                   "                </recurring>\n" +
+                                   "                <usages/>\n" +
+                                   "            </finalPhase>\n" +
+                                   "            <plansAllowedInBundle>1</plansAllowedInBundle>\n" +
+                                   "        </plan>\n" +
+                                   "        <plan name=\"super-monthly\">\n" +
+                                   "            <product>Super</product>\n" +
+                                   "            <initialPhases>\n" +
+                                   "                <phase type=\"TRIAL\">\n" +
+                                   "                    <duration>\n" +
+                                   "                        <unit>DAYS</unit>\n" +
+                                   "                        <number>30</number>\n" +
+                                   "                    </duration>\n" +
+                                   "                    <fixed type=\"ONE_TIME\">\n" +
+                                   "                        <fixedPrice>\n" +
+                                   "                            <price>\n" +
+                                   "<currency>USD</currency>\n" +
+                                   "<value>0</value>\n" +
+                                   "                            </price>\n" +
+                                   "                            <price>\n" +
+                                   "<currency>GBP</currency>\n" +
+                                   "<value>0</value>\n" +
+                                   "                            </price>\n" +
+                                   "                        </fixedPrice>\n" +
+                                   "                    </fixed>\n" +
+                                   "                    <usages/>\n" +
+                                   "                </phase>\n" +
+                                   "            </initialPhases>\n" +
+                                   "            <finalPhase type=\"EVERGREEN\">\n" +
+                                   "                <duration>\n" +
+                                   "                    <unit>UNLIMITED</unit>\n" +
+                                   "                    <number>-1</number>\n" +
+                                   "                </duration>\n" +
+                                   "                <recurring>\n" +
+                                   "                    <billingPeriod>MONTHLY</billingPeriod>\n" +
+                                   "                    <recurringPrice>\n" +
+                                   "                        <price>\n" +
+                                   "                            <currency>GBP</currency>\n" +
+                                   "                            <value>750.00</value>\n" +
+                                   "                        </price>\n" +
+                                   "                        <price>\n" +
+                                   "                            <currency>USD</currency>\n" +
+                                   "                            <value>1000.00</value>\n" +
+                                   "                        </price>\n" +
+                                   "                    </recurringPrice>\n" +
+                                   "                </recurring>\n" +
+                                   "                <usages/>\n" +
+                                   "            </finalPhase>\n" +
+                                   "            <plansAllowedInBundle>1</plansAllowedInBundle>\n" +
+                                   "        </plan>\n" +
+                                   "        <plan name=\"dynamic-annual\">\n" +
+                                   "            <product>Dynamic</product>\n" +
+                                   "            <initialPhases>\n" +
+                                   "                <phase type=\"TRIAL\">\n" +
+                                   "                    <duration>\n" +
+                                   "                        <unit>DAYS</unit>\n" +
+                                   "                        <number>14</number>\n" +
+                                   "                    </duration>\n" +
+                                   "                    <fixed type=\"ONE_TIME\">\n" +
+                                   "                        <fixedPrice>\n" +
+                                   "                            <price>\n" +
+                                   "<currency>USD</currency>\n" +
+                                   "<value>0</value>\n" +
+                                   "                            </price>\n" +
+                                   "                        </fixedPrice>\n" +
+                                   "                    </fixed>\n" +
+                                   "                    <usages/>\n" +
+                                   "                </phase>\n" +
+                                   "            </initialPhases>\n" +
+                                   "            <finalPhase type=\"EVERGREEN\">\n" +
+                                   "                <duration>\n" +
+                                   "                    <unit>UNLIMITED</unit>\n" +
+                                   "                    <number>-1</number>\n" +
+                                   "                </duration>\n" +
+                                   "                <recurring>\n" +
+                                   "                    <billingPeriod>MONTHLY</billingPeriod>\n" +
+                                   "                    <recurringPrice>\n" +
+                                   "                        <price>\n" +
+                                   "                            <currency>USD</currency>\n" +
+                                   "                            <value>10</value>\n" +
+                                   "                        </price>\n" +
+                                   "                    </recurringPrice>\n" +
+                                   "                </recurring>\n" +
+                                   "                <usages/>\n" +
+                                   "            </finalPhase>\n" +
+                                   "            <plansAllowedInBundle>1</plansAllowedInBundle>\n" +
+                                   "        </plan>\n" +
+                                   "    </plans>\n" +
+                                   "    <priceLists>\n" +
+                                   "        <defaultPriceList name=\"DEFAULT\">\n" +
+                                   "            <plans>\n" +
+                                   "                <plan>standard-monthly</plan>\n" +
+                                   "                <plan>sports-monthly</plan>\n" +
+                                   "                <plan>super-monthly</plan>\n" +
+                                   "                <plan>dynamic-annual</plan>\n" +
+                                   "            </plans>\n" +
+                                   "        </defaultPriceList>\n" +
+                                   "    </priceLists>\n" +
+                                   "</catalog>\n";
+
+        assertEquals(catalogUpdater.getCatalogXML(), expectedXML);
+        System.err.println(catalogUpdater.getCatalogXML());
+    }
+
+
+    private StandaloneCatalog enhanceOriginalCatalogForInvalidTestCases(final String catalogName) throws Exception {
+
+        final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource(catalogName).toExternalForm(), StandaloneCatalog.class);
+
+        final MutableStaticCatalog mutableCatalog = new DefaultMutableStaticCatalog(catalog);
+
+        final DefaultProduct newProduct1 = new DefaultProduct();
+        newProduct1.setName("Dynamic");
+        newProduct1.setCatagory(ProductCategory.BASE);
+        newProduct1.initialize((StandaloneCatalog) mutableCatalog, null);
+        mutableCatalog.addProduct(newProduct1);
+
+        final DefaultPlanPhase discountPhase1 = new DefaultPlanPhase();
+        discountPhase1.setPhaseType(PhaseType.DISCOUNT);
+        discountPhase1.setDuration(new DefaultDuration().setUnit(TimeUnit.DAYS).setNumber(14));
+        discountPhase1.setRecurring(new DefaultRecurring().setBillingPeriod(BillingPeriod.MONTHLY).setRecurringPrice(new DefaultInternationalPrice().setPrices(new DefaultPrice[]{new DefaultPrice().setCurrency(Currency.USD).setValue(BigDecimal.TEN)})));
+
+        final DefaultPlanPhase evergreenPhase1 = new DefaultPlanPhase();
+        evergreenPhase1.setPhaseType(PhaseType.EVERGREEN);
+        evergreenPhase1.setDuration(new DefaultDuration().setUnit(TimeUnit.MONTHS).setNumber(1));
+        evergreenPhase1.setRecurring(new DefaultRecurring().setBillingPeriod(BillingPeriod.MONTHLY).setRecurringPrice(new DefaultInternationalPrice().setPrices(new DefaultPrice[]{new DefaultPrice().setCurrency(Currency.USD).setValue(BigDecimal.TEN)})));
+
+        // Add a Plan with a DISCOUNT phase
+        final DefaultPlan newPlan1 = new DefaultPlan();
+        newPlan1.setName("dynamic-monthly");
+        newPlan1.setPriceListName(DefaultPriceListSet.DEFAULT_PRICELIST_NAME);
+        newPlan1.setProduct(newProduct1);
+        newPlan1.setInitialPhases(new DefaultPlanPhase[]{discountPhase1});
+        newPlan1.setFinalPhase(evergreenPhase1);
+        mutableCatalog.addPlan(newPlan1);
+        newPlan1.initialize((StandaloneCatalog) mutableCatalog, new URI("dummy"));
+
+
+        final DefaultProduct newProduct2 = new DefaultProduct();
+        newProduct2.setName("SuperDynamic");
+        newProduct2.setCatagory(ProductCategory.BASE);
+        newProduct2.initialize((StandaloneCatalog) mutableCatalog, null);
+        mutableCatalog.addProduct(newProduct2);
+
+        // Add a Plan with a FIXEDTERM phase
+        final DefaultPlanPhase fixedterm2 = new DefaultPlanPhase();
+        fixedterm2.setPhaseType(PhaseType.FIXEDTERM);
+        fixedterm2.setDuration(new DefaultDuration().setUnit(TimeUnit.MONTHS).setNumber(3));
+        fixedterm2.setRecurring(new DefaultRecurring().setBillingPeriod(BillingPeriod.MONTHLY).setRecurringPrice(new DefaultInternationalPrice().setPrices(new DefaultPrice[]{new DefaultPrice().setCurrency(Currency.USD).setValue(BigDecimal.TEN)})));
+
+
+        final DefaultPlan newPlan2 = new DefaultPlan();
+        newPlan2.setName("superdynamic-fixedterm");
+        newPlan2.setPriceListName(DefaultPriceListSet.DEFAULT_PRICELIST_NAME);
+        newPlan2.setProduct(newProduct2);
+        newPlan2.setFinalPhase(fixedterm2);
+        mutableCatalog.addPlan(newPlan2);
+        newPlan2.initialize((StandaloneCatalog) mutableCatalog, new URI("dummy"));
+
+
+        final String newCatalogStr = XMLWriter.writeXML((StandaloneCatalog) mutableCatalog, StandaloneCatalog.class);
+        return XMLLoader.getObjectFromStream(new URI("dummy"), new ByteArrayInputStream(newCatalogStr.getBytes(Charset.forName("UTF-8"))), StandaloneCatalog.class);
+    }
+
+
+    private void addBadSimplePlanDescriptor(final CatalogUpdater catalogUpdater, final SimplePlanDescriptor desc) {
+        try {
+            catalogUpdater.addSimplePlanDescriptor(desc);
+            fail("Should have failed to add invalid desc " + desc);
+        } catch (final CatalogApiException e) {
+            assertEquals(e.getCode(), ErrorCode.CAT_FAILED_SIMPLE_PLAN_VALIDATION.getCode());
+        }
+    }
+}
\ No newline at end of file
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
index 26290f9..f26c167 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
@@ -191,17 +191,6 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
         logCreateEntitlementWithAOs(log, externalKey, entitlementSpecifiers, entitlementEffectiveDate, billingEffectiveDate);
 
 
-        final EntitlementSpecifier baseSpecifier = Iterables.tryFind(entitlementSpecifiers, new Predicate<EntitlementSpecifier>() {
-            @Override
-            public boolean apply(final EntitlementSpecifier specifier) {
-                return specifier.getPlanPhaseSpecifier() != null && ProductCategory.BASE.equals(specifier.getPlanPhaseSpecifier().getProductCategory());
-            }
-        }).orNull();
-
-        if (baseSpecifier == null) {
-            throw new EntitlementApiException(new IllegalArgumentException(), ErrorCode.SUB_CREATE_NO_BP.getCode(), "Missing Base Subscription.");
-        }
-
         final List<EntitlementSpecifier> entitlementSpecifierList = new ArrayList<EntitlementSpecifier>();
         Iterables.addAll(entitlementSpecifierList, entitlementSpecifiers);
 
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
index ba5f169..bd6e009 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
@@ -378,7 +378,6 @@ public class EventsStreamBuilder {
             phaseType = subscription.getLastActivePhase().getPhaseType();
         }
         return new PlanPhaseSpecifier(lastActiveProductName,
-                                      productCategory,
                                       billingPeriod,
                                       priceListName,
                                       phaseType);
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 4b54799..743cc8a 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
@@ -42,7 +42,7 @@ public abstract class EntitlementLoggingHelper {
 
         if (log.isInfoEnabled()) {
             final StringBuilder logLine = new StringBuilder("Create ")
-                    .append(bundleId != null ? " BP " : " AO ")
+                    .append(bundleId != null ? " AO " : " BP ")
                     .append("Entitlement: ");
 
             if (bundleId != null) {
@@ -322,8 +322,6 @@ public abstract class EntitlementLoggingHelper {
             if (addParentheseQuote) {
                 logLine.append("'(");
             }
-            logLine.append(spec.getProductCategory() != null ? spec.getProductCategory() : "null");
-            logLine.append(":");
             logLine.append(spec.getProductName() != null ? spec.getProductName() : "null");
             logLine.append(":");
             logLine.append(spec.getBillingPeriod() != null ? spec.getBillingPeriod() : "null");
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java
index 327046c..15cb7cb 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java
@@ -51,7 +51,7 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -79,7 +79,7 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -112,7 +112,7 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -145,7 +145,7 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -170,7 +170,7 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -213,7 +213,7 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -253,7 +253,7 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -290,7 +290,7 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = accountApi.createAccount(getAccountData(7), callContext);
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, startDate, startDate, false, ImmutableList.<PluginProperty>of(), callContext);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
index a963f6b..fe59e7d 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
@@ -58,7 +58,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Keep the same object for the whole test, to make sure we refresh its state before r/w calls
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -67,7 +67,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         // Add ADD_ON
         // Keep the same object for the whole test, to make sure we refresh its state before r/w calls
-        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
         final Entitlement addOnEntitlement = entitlementApi.addEntitlement(entitlement.getBundleId(), addOnSpec, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
@@ -121,7 +121,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Keep the same object for the whole test, to make sure we refresh its state before r/w calls
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -154,7 +154,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -246,7 +246,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -254,7 +254,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         assertListenerStatus();
 
         // Add ADD_ON
-        final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
         final Entitlement telescopicEntitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
@@ -285,7 +285,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -294,7 +294,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         clock.addDays(1);
         final LocalDate effectiveDateSpec1 = new LocalDate(clock.getUTCNow(), account.getTimeZone());
-        final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
         final Entitlement telescopicEntitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, effectiveDateSpec1, effectiveDateSpec1, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
@@ -321,7 +321,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         // Try to add an ADD_ON, it should fail
         try {
-            final PlanPhaseSpecifier spec3 = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+            final PlanPhaseSpecifier spec3 = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
             final Entitlement telescopicEntitlement3 = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, effectiveDateSpec1, effectiveDateSpec1, false, ImmutableList.<PluginProperty>of(), callContext);
         } catch (EntitlementApiException e) {
             assertEquals(e.getCode(), ErrorCode.SUB_GET_NO_SUCH_BASE_SUBSCRIPTION.getCode());
@@ -360,7 +360,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         // Create entitlement
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -413,7 +413,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         // internal context will be configured for accountSrc
         final Account accountSrc = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement (with migrated flag so we can check later that transferred subscription is in right status)
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -461,7 +461,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Keep the same object for the whole test, to make sure we refresh its state before r/w calls
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.PHASE);
@@ -506,8 +506,8 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Cleaning", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Cleaning", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final String externalKey = "baseExternalKey";
         EntitlementSpecifier baseEntitlementSpecifier = new DefaultEntitlementSpecifier(baseSpec, null);
@@ -544,8 +544,8 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Invalid", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Invalid",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final String externalKey = "baseExternalKey";
         EntitlementSpecifier baseEntitlementSpecifier = new DefaultEntitlementSpecifier(baseSpec, null);
@@ -574,8 +574,8 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Cleaning", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Bullets", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Cleaning",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Bullets",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final String externalKey = "addOnExternalKey";
         EntitlementSpecifier addOnEntitlementSpecifier1 = new DefaultEntitlementSpecifier(baseSpec, null);
@@ -607,7 +607,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         final LocalDate entitlementDate = initialDate.plusDays(3);
         final LocalDate billingDate = initialDate.plusDays(5);
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
@@ -635,7 +635,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         final LocalDate entitlementDate = initialDate.plusDays(3);
         final LocalDate billingDate = null;
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         testListener.pushExpectedEvents(NextEvent.CREATE);
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
@@ -662,7 +662,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         final LocalDate entitlementDate = null;
         final LocalDate billingDate = initialDate.plusDays(5);
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         testListener.pushExpectedEvents(NextEvent.BLOCK);
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
@@ -686,7 +686,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         final LocalDate entitlementDate = initialDate.minusDays(3);
         final LocalDate billingDate = initialDate.minusDays(5);
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CREATE);
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
@@ -706,7 +706,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         final LocalDate entitlementDate = initialDate.minusDays(3);
         final LocalDate billingDate = null;
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CREATE);
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
@@ -726,7 +726,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         final LocalDate entitlementDate = null;
         final LocalDate billingDate = initialDate.minusDays(5);
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CREATE);
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
index 4d6c5f2..aef34c1 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
@@ -59,7 +59,7 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
         clock.setDay(initialDate);
 
         final Account account = createAccount(getAccountData(7));
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
         final Entitlement entitlement1 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         // Sleep 1 sec so created date are apart from each other and ordering in the bundle does not default on the UUID which is random.
@@ -110,7 +110,7 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -144,7 +144,7 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
 
         clock.addDays(1);
         // Re-create a new bundle with same externalKey
-        final PlanPhaseSpecifier spec2 = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec2 = new PlanPhaseSpecifier("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -218,7 +218,7 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
 
         // Create entitlement
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -271,7 +271,7 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         //2013-08-07
         final LocalDate effectiveDate = initialDate.plusMonths(1);
@@ -304,7 +304,7 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final LocalDate futureDate = new LocalDate(2013, 9, 1);
 
@@ -334,7 +334,7 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
 
         final Account account = accountApi.createAccount(getAccountData(7), callContext);
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
         final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
@@ -408,7 +408,7 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
@@ -482,7 +482,7 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java
index ae7f4ba..ae12fdc 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java
@@ -63,7 +63,7 @@ public class TestRegessionSubscriptionApi extends EntitlementTestSuiteWithEmbedd
         final LocalDate entitlementEffectiveDate = initialDate.minusDays(1);
 
         final Account account = createAccount(getAccountData(7));
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, entitlementEffectiveDate, null, false, ImmutableList.<PluginProperty>of(), callContext);
         // Because of the BlockingState event ENT_STARTED, the entitlement date should be correctly set
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
index e5f00d3..d96a38c 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
@@ -135,7 +135,7 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
         subscriptionApi.addBlockingState(state1, null, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
         Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestDefaultBlockingStateDao.java b/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestDefaultBlockingStateDao.java
index b22454a..c15abba 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestDefaultBlockingStateDao.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestDefaultBlockingStateDao.java
@@ -58,7 +58,7 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         // This is a simple smoke test at the dao level only to make sure we do sane
         // things in case there are no future add-on cancellation events to add in the stream.
         // See TestEntitlementUtils for a more comprehensive test
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java b/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java
index 4ca0b7a..589189b 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java
@@ -72,13 +72,13 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
 
         // Create base entitlement
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
-        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         baseEntitlement = (DefaultEntitlement) entitlementApi.createBaseEntitlement(account.getId(), baseSpec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         // Add ADD_ON
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
-        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         addOnEntitlement = (DefaultEntitlement) entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOnSpec, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -266,7 +266,7 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
     public void testChangePlanEOTWith2AddOns() throws Exception {
         // Add a second ADD_ON (Laser-Scope is available, not included)
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
-        final PlanPhaseSpecifier secondAddOnSpec = new PlanPhaseSpecifier("Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier secondAddOnSpec = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         final DefaultEntitlement secondAddOnEntitlement = (DefaultEntitlement) entitlementApi.addEntitlement(baseEntitlement.getBundleId(), secondAddOnSpec, null, clock.getUTCToday(), clock.getUTCToday(), false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -325,7 +325,7 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
     public void testCancelAddonsWhenBaseEntitlementIsCancelled() throws Exception {
         // Add a second ADD_ON
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.PHASE);
-        final PlanPhaseSpecifier addOn2Spec = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOn2Spec = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         final Entitlement addOn2Entitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOn2Spec, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
index 3ee58a2..bd5d6a6 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
@@ -186,7 +186,7 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
     private Catalog initCatalog(final CatalogService catalogService) throws Exception {
 
         ((DefaultCatalogService) catalogService).loadCatalog();
-        final Catalog catalog = catalogService.getFullCatalog(internalCallContext);
+        final Catalog catalog = catalogService.getFullCatalog(true, internalCallContext);
         assertNotNull(catalog);
         return catalog;
     }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index d9cbe1c..908e709 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -556,4 +556,16 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
         dao.transferChildCreditToParent(childAccount, internalCallContext);
 
     }
+
+    @Override
+    public List<InvoiceItem> getInvoiceItemsByParentInvoice(final UUID parentInvoiceId, final TenantContext context) throws InvoiceApiException {
+        final InternalTenantContext  internalTenantContext = internalCallContextFactory.createInternalTenantContext(parentInvoiceId, ObjectType.INVOICE, context);
+        return ImmutableList.copyOf(Collections2.transform(dao.getInvoiceItemsByParentInvoice(parentInvoiceId, internalTenantContext),
+                                                                    new Function<InvoiceItemModelDao, InvoiceItem>() {
+            @Override
+            public InvoiceItem apply(final InvoiceItemModelDao input) {
+                return InvoiceItemFactory.fromModelDao(input);
+            }
+        }));
+    }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index 5ee6b8e..204a0e5 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -1175,4 +1175,15 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
             }
         });
     }
+
+    @Override
+    public List<InvoiceItemModelDao> getInvoiceItemsByParentInvoice(final UUID parentInvoiceId, final InternalTenantContext context) throws InvoiceApiException {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceItemModelDao>>() {
+            @Override
+            public List<InvoiceItemModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceItemSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+                return transactional.getInvoiceItemsByParentInvoice(parentInvoiceId.toString(), context);
+            }
+        });
+    }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
index d1d16f1..ca7c49b 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
@@ -203,4 +203,14 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
      * @throws InvoiceApiException if any unexpected error occurs
      */
     void transferChildCreditToParent(Account childAccount, InternalCallContext childAccountContext) throws InvoiceApiException;
+
+    /**
+     * Retrieve invoice items details associated to Parent SUMMARY invoice item
+     *
+     * @param parentInvoiceId the parent invoice id
+     * @param context the tenant context
+     * @return a list of invoice items associated with a parent invoice
+     * @throws InvoiceApiException if any unexpected error occurs
+     */
+    List<InvoiceItemModelDao> getInvoiceItemsByParentInvoice(UUID parentInvoiceId, final InternalTenantContext context) throws InvoiceApiException;
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
index 8278a98..e30b5d2 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
@@ -53,4 +53,8 @@ public interface InvoiceItemSqlDao extends EntitySqlDao<InvoiceItemModelDao, Inv
     void updateAmount(@Bind("id") String invoiceItemId,
                       @Bind("amount")BigDecimal amount,
                       @BindBean final InternalCallContext context);
+
+    @SqlQuery
+    List<InvoiceItemModelDao> getInvoiceItemsByParentInvoice(@Bind("parentInvoiceId") final String parentInvoiceId,
+                                                             @BindBean final InternalTenantContext context);
 }
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
index 0542071..ae19a63 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
@@ -75,4 +75,14 @@ updateAmount() ::= <<
     SET amount = :amount
     WHERE id = :id
     <AND_CHECK_TENANT()>;
+>>
+
+getInvoiceItemsByParentInvoice() ::= <<
+  SELECT <allTableFields(("items."))>
+  FROM <tableName()> items
+  INNER JOIN invoice_parent_children invRel ON invRel.child_invoice_id = items.invoice_id
+  WHERE invRel.parent_invoice_id = :parentInvoiceId
+  <AND_CHECK_TENANT("items.")>
+  <defaultOrderBy()>
+  ;
 >>
\ No newline at end of file
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
index 9148a33..8ba0cc8 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
@@ -405,4 +405,9 @@ public class MockInvoiceDao extends MockEntityDaoBase<InvoiceModelDao, Invoice, 
     public void transferChildCreditToParent(final Account childAccount, final InternalCallContext context) throws InvoiceApiException {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public List<InvoiceItemModelDao> getInvoiceItemsByParentInvoice(final UUID parentInvoiceId, final InternalTenantContext context) throws InvoiceApiException {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
index 7bb3b93..b26dbbb 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
@@ -63,6 +63,7 @@ import org.killbill.billing.invoice.model.DefaultInvoice;
 import org.killbill.billing.invoice.model.DefaultInvoicePayment;
 import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
 import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
+import org.killbill.billing.invoice.model.ItemAdjInvoiceItem;
 import org.killbill.billing.invoice.model.ParentInvoiceItem;
 import org.killbill.billing.invoice.model.RecurringInvoiceItem;
 import org.killbill.billing.invoice.model.RepairAdjInvoiceItem;
@@ -1761,4 +1762,33 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
 
     }
 
+    @Test(groups = "slow")
+    public void testRetrieveInvoiceItemsByParentInvoice() throws InvoiceApiException {
+        final UUID childAccountId = account.getId();
+        final Invoice childInvoice = new DefaultInvoice(childAccountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
+        final UUID invoiceId = childInvoice.getId();
+        final UUID subscriptionId = UUID.randomUUID();
+        final UUID bundleId = UUID.randomUUID();
+        final LocalDate startDate = new LocalDate(2010, 1, 1);
+        final LocalDate endDate = new LocalDate(2010, 4, 1);
+        final InvoiceItem invoiceItem = new RecurringInvoiceItem(invoiceId, childAccountId, bundleId, subscriptionId, "test plan", "test phase", startDate, endDate,
+                                                                 new BigDecimal("21.00"), new BigDecimal("7.00"), Currency.USD);
+        final InvoiceItem invoiceAdj = new ItemAdjInvoiceItem(invoiceItem, startDate, new BigDecimal("-5.00"), Currency.USD);
+
+        childInvoice.addInvoiceItem(invoiceItem);
+        childInvoice.addInvoiceItem(invoiceAdj);
+        invoiceUtil.createInvoice(childInvoice, true, context);
+
+        final UUID parentInvoiceId = UUID.randomUUID();
+
+        InvoiceParentChildModelDao invoiceRelation = new InvoiceParentChildModelDao(parentInvoiceId, childInvoice.getId(), childAccountId);
+        invoiceDao.createParentChildInvoiceRelation(invoiceRelation, context);
+
+        final List<InvoiceItemModelDao> invoiceItems = invoiceDao.getInvoiceItemsByParentInvoice(parentInvoiceId, context);
+        assertEquals(invoiceItems.size(), 2);
+        assertEquals(invoiceItems.get(0).getType(), InvoiceItemType.RECURRING);
+        assertEquals(invoiceItems.get(1).getType(), InvoiceItemType.ITEM_ADJ);
+
+    }
+
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/GatewayNotificationJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/GatewayNotificationJson.java
index 33cfa31..b8ef5ca 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/GatewayNotificationJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/GatewayNotificationJson.java
@@ -37,14 +37,14 @@ public class GatewayNotificationJson extends JsonBase {
     private final Integer status;
     private final String entity;
     private final Map<String, List<String>> headers;
-    private final Map<String, String> properties;
+    private final Map<String, Object> properties;
 
     @JsonCreator
     public GatewayNotificationJson(@JsonProperty("kbPaymentId") final String kbPaymentId,
                                    @JsonProperty("status") final Integer status,
                                    @JsonProperty("entity") final String entity,
                                    @JsonProperty("headers") final Map<String, List<String>> headers,
-                                   @JsonProperty("properties") final Map<String, String> properties) {
+                                   @JsonProperty("properties") final Map<String, Object> properties) {
         this.kbPaymentId = kbPaymentId;
         this.status = status;
         this.entity = entity;
@@ -94,7 +94,7 @@ public class GatewayNotificationJson extends JsonBase {
         return headers;
     }
 
-    public Map<String, String> getProperties() {
+    public Map<String, Object> getProperties() {
         return properties;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/HostedPaymentPageFormDescriptorJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/HostedPaymentPageFormDescriptorJson.java
index 311b475..24b292c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/HostedPaymentPageFormDescriptorJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/HostedPaymentPageFormDescriptorJson.java
@@ -31,15 +31,15 @@ public class HostedPaymentPageFormDescriptorJson extends JsonBase {
     private final String kbAccountId;
     private final String formMethod;
     private final String formUrl;
-    private final Map<String, String> formFields;
-    private final Map<String, String> properties;
+    private final Map<String, Object> formFields;
+    private final Map<String, Object> properties;
 
     @JsonCreator
     public HostedPaymentPageFormDescriptorJson(@JsonProperty("kbAccountId") final String kbAccountId,
                                                @JsonProperty("formMethod") final String formMethod,
                                                @JsonProperty("formUrl") final String formUrl,
-                                               @JsonProperty("formFields") final Map<String, String> formFields,
-                                               @JsonProperty("properties") final Map<String, String> properties) {
+                                               @JsonProperty("formFields") final Map<String, Object> formFields,
+                                               @JsonProperty("properties") final Map<String, Object> properties) {
         this.kbAccountId = kbAccountId;
         this.formMethod = formMethod;
         this.formUrl = formUrl;
@@ -67,11 +67,11 @@ public class HostedPaymentPageFormDescriptorJson extends JsonBase {
         return formUrl;
     }
 
-    public Map<String, String> getFormFields() {
+    public Map<String, Object> getFormFields() {
         return formFields;
     }
 
-    public Map<String, String> getProperties() {
+    public Map<String, Object> getProperties() {
         return properties;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceItemJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceItemJson.java
index 55bf4d5..0d9c15c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceItemJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceItemJson.java
@@ -31,6 +31,9 @@ import org.killbill.billing.util.audit.AuditLog;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import io.swagger.annotations.ApiModelProperty;
 
 public class InvoiceItemJson extends JsonBase {
@@ -58,6 +61,7 @@ public class InvoiceItemJson extends JsonBase {
     private final LocalDate endDate;
     private final BigDecimal amount;
     private final String currency;
+    private List<InvoiceItemJson> childItems;
 
     @JsonCreator
     public InvoiceItemJson(@JsonProperty("invoiceItemId") final String invoiceItemId,
@@ -76,6 +80,7 @@ public class InvoiceItemJson extends JsonBase {
                            @JsonProperty("endDate") final LocalDate endDate,
                            @JsonProperty("amount") final BigDecimal amount,
                            @JsonProperty("currency") final String currency,
+                           @JsonProperty("childItems") final List<InvoiceItemJson> childItems,
                            @JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
         super(auditLogs);
         this.invoiceItemId = invoiceItemId;
@@ -94,14 +99,27 @@ public class InvoiceItemJson extends JsonBase {
         this.endDate = endDate;
         this.amount = amount;
         this.currency = currency;
+        this.childItems = childItems;
     }
 
-    public InvoiceItemJson(final InvoiceItem item, @Nullable final List<AuditLog> auditLogs) {
+    public InvoiceItemJson(final InvoiceItem item, final List<InvoiceItem> childItems, @Nullable final List<AuditLog> auditLogs) {
         this(toString(item.getId()), toString(item.getInvoiceId()), toString(item.getLinkedItemId()),
              toString(item.getAccountId()), toString(item.getChildAccountId()), toString(item.getBundleId()), toString(item.getSubscriptionId()),
              item.getPlanName(), item.getPhaseName(), item.getUsageName(), item.getInvoiceItemType().toString(),
              item.getDescription(), item.getStartDate(), item.getEndDate(),
-             item.getAmount(), item.getCurrency().name(), toAuditLogJson(auditLogs));
+             item.getAmount(), item.getCurrency().name(), toInvoiceItemJson(childItems), toAuditLogJson(auditLogs));
+    }
+
+    private static List<InvoiceItemJson> toInvoiceItemJson(final List<InvoiceItem> childItems) {
+        if (childItems == null) {
+            return null;
+        }
+        return ImmutableList.copyOf(Collections2.transform(childItems, new Function<InvoiceItem, InvoiceItemJson>() {
+            @Override
+            public InvoiceItemJson apply(final InvoiceItem input) {
+                return new InvoiceItemJson(input);
+            }
+        }));
     }
 
     public InvoiceItem toInvoiceItem() {
@@ -209,7 +227,7 @@ public class InvoiceItemJson extends JsonBase {
     }
 
     public InvoiceItemJson(final InvoiceItem input) {
-        this(input, null);
+        this(input, null, null);
     }
 
     public String getInvoiceItemId() {
@@ -276,6 +294,10 @@ public class InvoiceItemJson extends JsonBase {
         return currency;
     }
 
+    public List<InvoiceItemJson> getChildItems() {
+        return childItems;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
@@ -295,6 +317,7 @@ public class InvoiceItemJson extends JsonBase {
         sb.append(", endDate=").append(endDate);
         sb.append(", amount=").append(amount);
         sb.append(", currency=").append(currency);
+        sb.append(", childItems=").append(childItems);
         sb.append('}');
         return sb.toString();
     }
@@ -316,8 +339,7 @@ public class InvoiceItemJson extends JsonBase {
         if (childAccountId != null ? !childAccountId.equals(that.childAccountId) : that.childAccountId != null) {
             return false;
         }
-        if (!((amount == null && that.amount == null) ||
-              (amount != null && that.amount != null && amount.compareTo(that.amount) == 0))) {
+        if (amount != null ? amount.compareTo(that.amount) != 0 : that.amount != null) {
             return false;
         }
         if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
@@ -358,6 +380,9 @@ public class InvoiceItemJson extends JsonBase {
         if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
             return false;
         }
+        if (childItems != null ? !childItems.equals(that.childItems) : that.childItems != null) {
+            return false;
+        }
 
         return true;
     }
@@ -379,6 +404,7 @@ public class InvoiceItemJson extends JsonBase {
         result = 31 * result + (endDate != null ? endDate.hashCode() : 0);
         result = 31 * result + (amount != null ? amount.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (childItems != null ? childItems.hashCode() : 0);
         return result;
     }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java
index 9ef5ac9..7b002a3 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java
@@ -22,15 +22,19 @@ import java.util.List;
 
 import javax.annotation.Nullable;
 
+import org.apache.shiro.util.CollectionUtils;
 import org.joda.time.LocalDate;
-
 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.util.audit.AccountAuditLogs;
 import org.killbill.billing.util.audit.AuditLog;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 import io.swagger.annotations.ApiModelProperty;
 
 public class InvoiceJson extends JsonBase {
@@ -89,7 +93,7 @@ public class InvoiceJson extends JsonBase {
     }
 
     public InvoiceJson(final Invoice input) {
-        this(input, false, null);
+        this(input, false, null, null);
     }
 
     public InvoiceJson(final Invoice input, final String bundleKeys, final List<CreditJson> credits, final List<AuditLog> auditLogs) {
@@ -98,12 +102,21 @@ public class InvoiceJson extends JsonBase {
              input.getBalance(), input.getAccountId().toString(), bundleKeys, credits, null, input.isParentInvoice(), toAuditLogJson(auditLogs));
     }
 
-    public InvoiceJson(final Invoice input, final boolean withItems, @Nullable final AccountAuditLogs accountAuditLogs) {
+    public InvoiceJson(final Invoice input, final boolean withItems, final List<InvoiceItem> childItems, @Nullable final AccountAuditLogs accountAuditLogs) {
         super(toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForInvoice(input.getId())));
         this.items = new ArrayList<InvoiceItemJson>(input.getInvoiceItems().size());
-        if (withItems) {
+        if (withItems || !CollectionUtils.isEmpty(childItems)) {
             for (final InvoiceItem item : input.getInvoiceItems()) {
-                this.items.add(new InvoiceItemJson(item, accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForInvoiceItem(item.getId())));
+                ImmutableList<InvoiceItem> childItemsFiltered = null;
+                if (item.getInvoiceItemType().equals(InvoiceItemType.PARENT_SUMMARY) && !CollectionUtils.isEmpty(childItems)) {
+                    childItemsFiltered = ImmutableList.copyOf(Iterables.filter(childItems, new Predicate<InvoiceItem>() {
+                        @Override
+                        public boolean apply(@Nullable final InvoiceItem invoice) {
+                            return invoice.getAccountId().equals(item.getChildAccountId());
+                        }
+                    }));
+                }
+                this.items.add(new InvoiceItemJson(item, childItemsFiltered, accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForInvoiceItem(item.getId())));
             }
         }
         this.amount = input.getChargedAmount();
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/JsonBase.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/JsonBase.java
index a92f6e9..936cb71 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/JsonBase.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/JsonBase.java
@@ -74,11 +74,11 @@ public abstract class JsonBase {
         return properties;
     }
 
-    protected Map<String, String> propertiesToMap(final Iterable<PluginProperty> properties) {
-        final Map<String, String> propertiesMap = new HashMap<String, String>();
+    protected Map<String, Object> propertiesToMap(final Iterable<PluginProperty> properties) {
+        final Map<String, Object> propertiesMap = new HashMap<String, Object>();
         for (final PluginProperty pluginProperty : properties) {
             if (pluginProperty.getValue() != null) {
-                propertiesMap.put(pluginProperty.getKey(), pluginProperty.getValue().toString());
+                propertiesMap.put(pluginProperty.getKey(), pluginProperty.getValue());
             }
         }
         return propertiesMap;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PhasePriceOverrideJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PhasePriceOverrideJson.java
index 6f8745a..2365957 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PhasePriceOverrideJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PhasePriceOverrideJson.java
@@ -127,7 +127,7 @@ public class PhasePriceOverrideJson {
                     return new DefaultPlanPhasePriceOverride(input.getPhaseName(), currency, input.getFixedPrice(), input.getRecurringPrice());
                 } else {
                     final PhaseType phaseType = input.getPhaseType() != null ? PhaseType.valueOf(input.getPhaseType()) : null;
-                    final PlanPhaseSpecifier planPhaseSpecifier = new PlanPhaseSpecifier(spec.getProductName(), spec.getProductCategory(), spec.getBillingPeriod(), spec.getPriceListName(), phaseType);
+                    final PlanPhaseSpecifier planPhaseSpecifier = new PlanPhaseSpecifier(spec.getProductName(), spec.getBillingPeriod(), spec.getPriceListName(), phaseType);
                     return new DefaultPlanPhasePriceOverride(planPhaseSpecifier, currency, input.getFixedPrice(), input.getRecurringPrice());
                 }
             }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SimplePlanJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SimplePlanJson.java
new file mode 100644
index 0000000..12913cd
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SimplePlanJson.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.jaxrs.json;
+
+import java.math.BigDecimal;
+
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.TimeUnit;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class SimplePlanJson {
+
+    private final String planId;
+    private final String productName;
+    private final Currency currency;
+    private final BigDecimal amount;
+    private final BillingPeriod billingPeriod;
+    private final Integer trialLength;
+    private final TimeUnit trialTimeUnit;
+
+    @JsonCreator
+    public SimplePlanJson(@JsonProperty("planId")  final String planId,
+                          @JsonProperty("productName")  final String productName,
+                          @JsonProperty("currency")  final Currency currency,
+                          @JsonProperty("amount")  final BigDecimal amount,
+                          @JsonProperty("billingPeriod")  final BillingPeriod billingPeriod,
+                          @JsonProperty("trialLength")  final Integer trialLength,
+                          @JsonProperty("trialTimeUnit")  final TimeUnit trialTimeUnit) {
+        this.planId = planId;
+        this.productName = productName;
+        this.currency = currency;
+        this.amount = amount;
+        this.billingPeriod = billingPeriod;
+        this.trialLength = trialLength;
+        this.trialTimeUnit = trialTimeUnit;
+    }
+
+    public String getPlanId() {
+        return planId;
+    }
+
+    public String getProductName() {
+        return productName;
+    }
+
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public BillingPeriod getBillingPeriod() {
+        return billingPeriod;
+    }
+
+    public Integer getTrialLength() {
+        return trialLength;
+    }
+
+    public TimeUnit getTrialTimeUnit() {
+        return trialTimeUnit;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof SimplePlanJson)) {
+            return false;
+        }
+
+        final SimplePlanJson that = (SimplePlanJson) o;
+
+        if (planId != null ? !planId.equals(that.planId) : that.planId != null) {
+            return false;
+        }
+        if (productName != null ? !productName.equals(that.productName) : that.productName != null) {
+            return false;
+        }
+        if (currency != that.currency) {
+            return false;
+        }
+        if (amount != null ? amount.compareTo(that.amount) != 0 : that.amount != null) {
+            return false;
+        }
+        if (billingPeriod != that.billingPeriod) {
+            return false;
+        }
+        if (trialLength != null ? !trialLength.equals(that.trialLength) : that.trialLength != null) {
+            return false;
+        }
+        return trialTimeUnit == that.trialTimeUnit;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = planId != null ? planId.hashCode() : 0;
+        result = 31 * result + (productName != null ? productName.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (amount != null ? amount.hashCode() : 0);
+        result = 31 * result + (billingPeriod != null ? billingPeriod.hashCode() : 0);
+        result = 31 * result + (trialLength != null ? trialLength.hashCode() : 0);
+        result = 31 * result + (trialTimeUnit != null ? trialTimeUnit.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index f80e8e4..2fb32a1 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -614,7 +614,7 @@ public class AccountResource extends JaxRsResourceBase {
 
         final List<InvoiceJson> result = new LinkedList<InvoiceJson>();
         for (final Invoice invoice : invoices) {
-            result.add(new InvoiceJson(invoice, withItems, accountAuditLogs));
+            result.add(new InvoiceJson(invoice, withItems, null, accountAuditLogs));
         }
 
         return Response.status(Status.OK).entity(result).build();
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 6aaae90..f8ab371 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
@@ -40,9 +40,12 @@ import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogUserApi;
 import org.killbill.billing.catalog.api.Listing;
+import org.killbill.billing.catalog.api.SimplePlanDescriptor;
 import org.killbill.billing.catalog.api.StaticCatalog;
+import org.killbill.billing.catalog.api.user.DefaultSimplePlanDescriptor;
 import org.killbill.billing.jaxrs.json.CatalogJson;
 import org.killbill.billing.jaxrs.json.PlanDetailJson;
+import org.killbill.billing.jaxrs.json.SimplePlanJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.payment.api.PaymentApi;
@@ -179,4 +182,31 @@ public class CatalogResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(details).build();
     }
 
+
+    @TimedResource
+    @POST
+    @Path("/simplePlan")
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Upload the full catalog as XML")
+    @ApiResponses(value = {})
+    public Response addSimplePlan(final SimplePlanJson simplePlan,
+                                     @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                     @HeaderParam(HDR_REASON) final String reason,
+                                     @HeaderParam(HDR_COMMENT) final String comment,
+                                     @javax.ws.rs.core.Context final HttpServletRequest request,
+                                     @javax.ws.rs.core.Context final UriInfo uriInfo) throws Exception {
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor(simplePlan.getPlanId(),
+                                                                          simplePlan.getProductName(),
+                                                                          simplePlan.getCurrency(),
+                                                                          simplePlan.getAmount(),
+                                                                          simplePlan.getBillingPeriod(),
+                                                                          simplePlan.getTrialLength(),
+                                                                          simplePlan.getTrialTimeUnit());
+        catalogUserApi.addSimplePlan(desc, clock.getUTCNow(), callContext);
+        return uriBuilder.buildResponse(uriInfo, CatalogResource.class, null, null);
+    }
+
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index 89ee9dc..bc9ae22 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
@@ -176,16 +176,18 @@ public class InvoiceResource extends JaxRsResourceBase {
                            @ApiResponse(code = 404, message = "Invoice not found")})
     public Response getInvoice(@PathParam("invoiceId") final String invoiceId,
                                @QueryParam(QUERY_INVOICE_WITH_ITEMS) @DefaultValue("false") final boolean withItems,
+                               @QueryParam(QUERY_INVOICE_WITH_CHILDREN_ITEMS) @DefaultValue("false") final boolean withChildrenItems,
                                @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                @javax.ws.rs.core.Context final HttpServletRequest request) throws InvoiceApiException {
         final TenantContext tenantContext = context.createContext(request);
         final Invoice invoice = invoiceApi.getInvoice(UUID.fromString(invoiceId), tenantContext);
+        final List<InvoiceItem> childInvoiceItems = withChildrenItems ? invoiceApi.getInvoiceItemsByParentInvoice(invoice.getId(), tenantContext) : null;
         final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext);
 
         if (invoice == null) {
             throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
         } else {
-            final InvoiceJson json = new InvoiceJson(invoice, withItems, accountAuditLogs);
+            final InvoiceJson json = new InvoiceJson(invoice, withItems, childInvoiceItems, accountAuditLogs);
             return Response.status(Status.OK).entity(json).build();
         }
     }
@@ -198,16 +200,18 @@ public class InvoiceResource extends JaxRsResourceBase {
     @ApiResponses(value = {@ApiResponse(code = 404, message = "Invoice not found")})
     public Response getInvoiceByNumber(@PathParam("invoiceNumber") final Integer invoiceNumber,
                                        @QueryParam(QUERY_INVOICE_WITH_ITEMS) @DefaultValue("false") final boolean withItems,
+                                       @QueryParam(QUERY_INVOICE_WITH_CHILDREN_ITEMS) @DefaultValue("false") final boolean withChildrenItems,
                                        @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                        @javax.ws.rs.core.Context final HttpServletRequest request) throws InvoiceApiException {
         final TenantContext tenantContext = context.createContext(request);
         final Invoice invoice = invoiceApi.getInvoiceByNumber(invoiceNumber, tenantContext);
+        final List<InvoiceItem> childInvoiceItems = withChildrenItems ? invoiceApi.getInvoiceItemsByParentInvoice(invoice.getId(), tenantContext) : null;
         final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext);
 
         if (invoice == null) {
             throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceNumber);
         } else {
-            final InvoiceJson json = new InvoiceJson(invoice, withItems, accountAuditLogs);
+            final InvoiceJson json = new InvoiceJson(invoice, withItems, childInvoiceItems, accountAuditLogs);
             return Response.status(Status.OK).entity(json).build();
         }
     }
@@ -248,7 +252,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                                         if (accountsAuditLogs.get().get(invoice.getAccountId()) == null) {
                                                             accountsAuditLogs.get().put(invoice.getAccountId(), auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext));
                                                         }
-                                                        return new InvoiceJson(invoice, withItems, accountsAuditLogs.get().get(invoice.getAccountId()));
+                                                        return new InvoiceJson(invoice, withItems, null, accountsAuditLogs.get().get(invoice.getAccountId()));
                                                     }
                                                 },
                                                 nextPageUri
@@ -281,7 +285,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                                         if (accountsAuditLogs.get().get(invoice.getAccountId()) == null) {
                                                             accountsAuditLogs.get().put(invoice.getAccountId(), auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext));
                                                         }
-                                                        return new InvoiceJson(invoice, withItems, accountsAuditLogs.get().get(invoice.getAccountId()));
+                                                        return new InvoiceJson(invoice, withItems, null, accountsAuditLogs.get().get(invoice.getAccountId()));
                                                     }
                                                 },
                                                 nextPageUri
@@ -395,7 +399,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         try {
             final Invoice generatedInvoice = invoiceApi.triggerInvoiceGeneration(UUID.fromString(accountId), inputDate, dryRunArguments,
                                                                                  callContext);
-            return Response.status(Status.OK).entity(new InvoiceJson(generatedInvoice, true, null)).build();
+            return Response.status(Status.OK).entity(new InvoiceJson(generatedInvoice, true, null, null)).build();
         } catch (InvoiceApiException e) {
             if (e.getCode() == ErrorCode.INVOICE_NOTHING_TO_DO.getCode()) {
                 return Response.status(Status.NOT_FOUND).build();
@@ -565,6 +569,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                                    input.getEndDate(),
                                                    input.getAmount(),
                                                    accountCurrency.name(),
+                                                   null,
                                                    null);
                     }
                 }
@@ -1043,7 +1048,6 @@ public class InvoiceResource extends JaxRsResourceBase {
                                                                input.getProductCategory() != null &&
                                                                input.getBillingPeriod() != null) ?
                                                               new PlanPhaseSpecifier(input.getProductName(),
-                                                                                     ProductCategory.valueOf(input.getProductCategory()),
                                                                                      BillingPeriod.valueOf(input.getBillingPeriod()),
                                                                                      input.getPriceListName(),
                                                                                      input.getPhaseType() != null ? PhaseType.valueOf(input.getPhaseType()) : null) :
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index 43de419..0c833c8 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -96,6 +96,7 @@ public interface JaxrsResource {
     public static final String QUERY_INVOICE_WITH_ITEMS = "withItems";
     public static final String QUERY_WITH_MIGRATION_INVOICES = "withMigrationInvoices";
     public static final String QUERY_UNPAID_INVOICES_ONLY = "unpaidInvoicesOnly";
+    public static final String QUERY_INVOICE_WITH_CHILDREN_ITEMS = "withChildrenItems";
 
     public static final String QUERY_PAYMENT_EXTERNAL = "externalPayment";
     public static final String QUERY_PAYMENT_AMOUNT = "paymentAmount";
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
index f75cadf..d9df67f 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
@@ -195,13 +195,11 @@ public class SubscriptionResource extends JaxRsResourceBase {
                 final Account account = getAccountFromSubscriptionJson(entitlement, callContext);
                 final PhaseType phaseType = entitlement.getPhaseType() != null ? PhaseType.valueOf(entitlement.getPhaseType()) : null;
                 final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(entitlement.getProductName(),
-                                                                       ProductCategory.valueOf(entitlement.getProductCategory()),
                                                                        BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList(), phaseType);
 
                 final LocalDate resolvedEntitlementDate = requestedDate != null ? toLocalDate(requestedDate) : toLocalDate(entitlementDate);
                 final LocalDate resolvedBillingDate = requestedDate != null ? toLocalDate(requestedDate) : toLocalDate(billingDate);
                 final PlanSpecifier planSpec = new PlanSpecifier(entitlement.getProductName(),
-                                                                 ProductCategory.valueOf(entitlement.getProductCategory()),
                                                                  BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList());
 
                 final List<PlanPhasePriceOverride> overrides = PhasePriceOverrideJson.toPlanPhasePriceOverrides(entitlement.getPriceOverrides(), planSpec, account.getCurrency());
@@ -311,11 +309,9 @@ public class SubscriptionResource extends JaxRsResourceBase {
                 for (final SubscriptionJson entitlement : entitlements) {
 
                     final PlanPhaseSpecifier planPhaseSpecifier = new PlanPhaseSpecifier(entitlement.getProductName(),
-                                                                                         ProductCategory.valueOf(entitlement.getProductCategory()),
                                                                                          BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList(), null);
 
                     final PlanSpecifier planSpec = new PlanSpecifier(entitlement.getProductName(),
-                                                                     ProductCategory.valueOf(entitlement.getProductCategory()),
                                                                      BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList());
                     final List<PlanPhasePriceOverride> overrides = PhasePriceOverrideJson.toPlanPhasePriceOverrides(entitlement.getPriceOverrides(), planSpec, account.getCurrency());
 
@@ -419,7 +415,6 @@ public class SubscriptionResource extends JaxRsResourceBase {
 
                 final Account account = accountUserApi.getAccountById(current.getAccountId(), callContext);
                 final PlanSpecifier planSpec = new PlanSpecifier(entitlement.getProductName(),
-                                                                 ProductCategory.valueOf(entitlement.getProductCategory()),
                                                                  BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList());
                 final List<PlanPhasePriceOverride> overrides = PhasePriceOverrideJson.toPlanPhasePriceOverrides(entitlement.getPriceOverrides(), planSpec, account.getCurrency());
 
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java
index b4b33f9..bd54e91 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java
@@ -55,7 +55,7 @@ public class TestInvoiceItemJsonSimple extends JaxrsTestSuiteNoDB {
         final List<AuditLogJson> auditLogs = createAuditLogsJson(clock.getUTCNow());
         final InvoiceItemJson invoiceItemJson = new InvoiceItemJson(invoiceItemId, invoiceId, linkedInvoiceItemId, accountId, childAccountId,
                                                                                       bundleId, subscriptionId, planName, phaseName, usageName, type, description,
-                                                                                      startDate, endDate, amount, currency.name(), auditLogs);
+                                                                                      startDate, endDate, amount, currency.name(), null, auditLogs);
         Assert.assertEquals(invoiceItemJson.getInvoiceItemId(), invoiceItemId);
         Assert.assertEquals(invoiceItemJson.getInvoiceId(), invoiceId);
         Assert.assertEquals(invoiceItemJson.getLinkedInvoiceItemId(), linkedInvoiceItemId);
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
index f78edf5..98a0b01 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
@@ -197,7 +197,7 @@ public class BlockingCalculator {
     protected SortedSet<BillingEvent> createNewEvents(final List<DisabledDuration> disabledDuration, final SortedSet<BillingEvent> billingEvents, final SubscriptionBase subscription, final InternalTenantContext context) throws CatalogApiException {
 
         final SortedSet<BillingEvent> result = new TreeSet<BillingEvent>();
-        final Catalog catalog = catalogService.getFullCatalog(context);
+        final Catalog catalog = catalogService.getFullCatalog(true, context);
 
         for (final DisabledDuration duration : disabledDuration) {
             // The first one before the blocked duration
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
index e556f31..ba52d19 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
@@ -58,7 +58,6 @@ import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.bcd.BillCycleDayCalculator;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.Tag;
-import org.killbill.clock.Clock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -90,7 +89,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
 
     @Override
     public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(final UUID accountId, final DryRunArguments dryRunArguments, final InternalCallContext context) throws CatalogApiException, AccountApiException {
-        final StaticCatalog currentCatalog = catalogService.getCurrentCatalog(context);
+        final StaticCatalog currentCatalog = catalogService.getCurrentCatalog(true, context);
 
         // Check to see if billing is off for the account
         final List<Tag> accountTags = tagApi.getTags(accountId, ObjectType.ACCOUNT, context);
@@ -201,7 +200,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
                 return;
             }
 
-            final Catalog catalog = catalogService.getFullCatalog(context);
+            final Catalog catalog = catalogService.getFullCatalog(true, context);
 
             Integer overridenBCD = null;
             for (final EffectiveSubscriptionInternalEvent transition : billingTransitions) {
@@ -248,10 +247,9 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
         final BillingPeriod billingPeriod = phase.getRecurring() != null ? phase.getRecurring().getBillingPeriod() : BillingPeriod.NO_BILLING_PERIOD;
 
         return new PlanPhaseSpecifier(product.getName(),
-                               product.getCategory(),
-                               billingPeriod,
-                               transition.getNextPriceList(),
-                               phase.getPhaseType());
+                                      billingPeriod,
+                                      transition.getNextPriceList(),
+                                      phase.getPhaseType());
     }
 
     private boolean is_AUTO_INVOICING_OFF(final List<Tag> tags) {
diff --git a/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java b/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
index 568a786..b8dde33 100644
--- a/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
+++ b/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
@@ -125,7 +125,7 @@ public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestS
 
     private Catalog initCatalog(final CatalogService catalogService) throws Exception {
         ((DefaultCatalogService) catalogService).loadCatalog();
-        final Catalog catalog = catalogService.getFullCatalog(internalCallContext);
+        final Catalog catalog = catalogService.getFullCatalog(true, internalCallContext);
         assertNotNull(catalog);
         return catalog;
     }
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
index 2fc7a4b..e63aa9f 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
@@ -103,9 +103,9 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
         Mockito.when(subscriptionInternalApi.getBillingTransitions(Mockito.<SubscriptionBase>any(),  Mockito.<InternalTenantContext>any())).thenReturn(effectiveSubscriptionTransitions);
         Mockito.when(subscriptionInternalApi.getAllTransitions(Mockito.<SubscriptionBase>any(), Mockito.<InternalTenantContext>any())).thenReturn(effectiveSubscriptionTransitions);
 
-        catalog = ((MockCatalog) catalogService.getCurrentCatalog(internalCallContext));
+        catalog = ((MockCatalog) catalogService.getCurrentCatalog(true, internalCallContext));
         // TODO The MockCatalog module returns two different things for full vs current catalog
-        Mockito.when(catalogService.getFullCatalog(internalCallContext)).thenReturn(catalog);
+        Mockito.when(catalogService.getFullCatalog(true, internalCallContext)).thenReturn(catalog);
         // Set a default alignment
         catalog.setBillingAlignment(BillingAlignment.ACCOUNT);
 
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
index c5cc8a9..f72fb2f 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
@@ -57,7 +57,7 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
         final Account account = createAccount(getAccountData(7));
 
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(entitlement.getId(), internalCallContext);
         assertListenerStatus();
@@ -193,7 +193,7 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
         final Account account = createAccount(getAccountData(7));
 
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(entitlement.getId(), internalCallContext);
         assertListenerStatus();

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 5298d25..cb64711 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.116-SNAPSHOT</version>
+        <version>0.118-SNAPSHOT</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.17.2-SNAPSHOT</version>
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
index 7a24e1c..05de481 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
@@ -18,17 +18,22 @@
 
 package org.killbill.billing.jaxrs;
 
+import java.math.BigDecimal;
 import java.sql.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
 import org.joda.time.DateTime;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.TimeUnit;
 import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.model.Catalog;
 import org.killbill.billing.client.model.Plan;
 import org.killbill.billing.client.model.PlanDetail;
 import org.killbill.billing.client.model.Product;
+import org.killbill.billing.client.model.SimplePlan;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -95,4 +100,27 @@ public class TestCatalog extends TestJaxrsBase {
         Assert.fail();
     }
 
+    @Test(groups = "slow", description = "Can create a simple Plan into a per-tenant catalog")
+    public void testAddSimplePlan() throws Exception {
+
+        killBillClient.addSimplePan(new SimplePlan("foo-monthly", "Foo", Currency.USD, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED), requestOptions);
+        Catalog catalog = killBillClient.getJSONCatalog(requestOptions);
+        Assert.assertEquals(catalog.getProducts().size(),1);
+        Assert.assertEquals(catalog.getProducts().get(0).getName(),"Foo");
+        Assert.assertEquals(catalog.getPriceLists().size(),1);
+        Assert.assertEquals(catalog.getPriceLists().get(0).getName(), "DEFAULT");
+        Assert.assertEquals(catalog.getPriceLists().get(0).getPlans().size(), 1);
+        Assert.assertEquals(catalog.getPriceLists().get(0).getPlans().get(0), "foo-monthly");
+
+
+        killBillClient.addSimplePan(new SimplePlan("foo-annual", "Foo", Currency.USD, new BigDecimal("100.00"), BillingPeriod.ANNUAL, 0, TimeUnit.UNLIMITED), requestOptions);
+
+        catalog = killBillClient.getJSONCatalog(requestOptions);
+        Assert.assertEquals(catalog.getProducts().size(),1);
+        Assert.assertEquals(catalog.getProducts().get(0).getName(),"Foo");
+        Assert.assertEquals(catalog.getPriceLists().size(),1);
+        Assert.assertEquals(catalog.getPriceLists().get(0).getName(), "DEFAULT");
+        Assert.assertEquals(catalog.getPriceLists().get(0).getPlans().size(), 2);
+
+    }
 }
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 d6ad100..dfd8d8c 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
@@ -44,8 +44,8 @@ import org.killbill.billing.client.model.PaymentMethod;
 import org.killbill.billing.client.model.Payments;
 import org.killbill.billing.entitlement.api.SubscriptionEventType;
 import org.killbill.billing.invoice.api.DryRunType;
-import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.payment.provider.ExternalPaymentProviderPlugin;
 import org.killbill.billing.util.api.AuditLevel;
 import org.testng.Assert;
@@ -818,4 +818,93 @@ public class TestInvoice extends TestJaxrsBase {
 
     }
 
+    @Test(groups = "slow", description = "Can search and retrieve parent and children invoices with and without children items")
+    public void testParentInvoiceWithChildItems() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account parentAccount = createAccount();
+        final Account childAccount1 = createAccount(parentAccount.getAccountId());
+        final Account childAccount2 = createAccount(parentAccount.getAccountId());
+        final Account childAccount3 = createAccount(parentAccount.getAccountId());
+
+        // Add a bundle, subscription and move the clock to get the first invoice
+        createEntitlement(childAccount1.getAccountId(), UUID.randomUUID().toString(), "Shotgun",
+                          ProductCategory.BASE, BillingPeriod.MONTHLY, true);
+        createEntitlement(childAccount2.getAccountId(), UUID.randomUUID().toString(), "Pistol",
+                          ProductCategory.BASE, BillingPeriod.MONTHLY, true);
+        createEntitlement(childAccount3.getAccountId(), UUID.randomUUID().toString(), "Shotgun",
+                          ProductCategory.BASE, BillingPeriod.MONTHLY, true);
+
+
+        clock.addDays(32);
+        crappyWaitForLackOfProperSynchonization();
+
+        final List<Invoice> child1Invoices = killBillClient.getInvoicesForAccount(childAccount1.getAccountId(), true, false, requestOptions);
+        final List<Invoice> child2Invoices = killBillClient.getInvoicesForAccount(childAccount2.getAccountId(), true, false, requestOptions);
+        final List<Invoice> child3Invoices = killBillClient.getInvoicesForAccount(childAccount3.getAccountId(), true, false, requestOptions);
+
+        assertEquals(child1Invoices.size(), 2);
+        final Invoice child1RecurringInvoice = child1Invoices.get(1);
+        final InvoiceItem child1RecurringInvoiceItem = child1RecurringInvoice.getItems().get(0);
+        final InvoiceItem child2RecurringInvoiceItem = child2Invoices.get(1).getItems().get(0);
+        final InvoiceItem child3RecurringInvoiceItem = child3Invoices.get(1).getItems().get(0);
+
+        final List<Invoice> parentInvoices = killBillClient.getInvoicesForAccount(parentAccount.getAccountId(), true, false, requestOptions);
+        assertEquals(parentInvoices.size(), 2);
+
+        // check parent invoice with child invoice items and no adjustments
+        // parameters: withItems = true, withChildrenItems = true
+        Invoice parentInvoiceWithChildItems = killBillClient.getInvoice(parentInvoices.get(1).getInvoiceId(), true, true, requestOptions);
+        assertEquals(parentInvoiceWithChildItems.getItems().size(), 3);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(0).getChildItems().size(), 1);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(1).getChildItems().size(), 1);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(2).getChildItems().size(), 1);
+
+        // add an item adjustment
+        final InvoiceItem adjustmentInvoiceItem = new InvoiceItem();
+        adjustmentInvoiceItem.setAccountId(childAccount1.getAccountId());
+        adjustmentInvoiceItem.setInvoiceId(child1RecurringInvoice.getInvoiceId());
+        adjustmentInvoiceItem.setInvoiceItemId(child1RecurringInvoiceItem.getInvoiceItemId());
+        adjustmentInvoiceItem.setAmount(BigDecimal.TEN);
+        adjustmentInvoiceItem.setCurrency(child1RecurringInvoiceItem.getCurrency());
+        final Invoice invoiceAdjustment = killBillClient.adjustInvoiceItem(adjustmentInvoiceItem, requestOptions);
+        final InvoiceItem child1AdjInvoiceItem = killBillClient.getInvoice(invoiceAdjustment.getInvoiceId(), requestOptions).getItems().get(1);
+
+        // check parent invoice with child invoice items and adjustments
+        // parameters: withItems = true, withChildrenItems = true
+        parentInvoiceWithChildItems = killBillClient.getInvoice(parentInvoices.get(1).getInvoiceId(), true, true, requestOptions);
+        assertEquals(parentInvoiceWithChildItems.getItems().size(), 3);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(0).getChildItems().size(), 2);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(1).getChildItems().size(), 1);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(2).getChildItems().size(), 1);
+
+        final InvoiceItem child1InvoiceItemFromParent = parentInvoiceWithChildItems.getItems().get(0).getChildItems().get(0);
+        final InvoiceItem child1AdjInvoiceItemFromParent = parentInvoiceWithChildItems.getItems().get(0).getChildItems().get(1);
+        final InvoiceItem child2InvoiceItemFromParent = parentInvoiceWithChildItems.getItems().get(1).getChildItems().get(0);
+        final InvoiceItem child3InvoiceItemFromParent = parentInvoiceWithChildItems.getItems().get(2).getChildItems().get(0);
+
+        // check children items for each PARENT_SUMMARY item
+        assertTrue(child1InvoiceItemFromParent.equals(child1RecurringInvoiceItem));
+        assertTrue(child1AdjInvoiceItemFromParent.equals(child1AdjInvoiceItem));
+        assertTrue(child2InvoiceItemFromParent.equals(child2RecurringInvoiceItem));
+        assertTrue(child3InvoiceItemFromParent.equals(child3RecurringInvoiceItem));
+
+        // check parent invoice without child invoice items
+        parentInvoiceWithChildItems = killBillClient.getInvoice(parentInvoices.get(1).getInvoiceId(), true, false, requestOptions);
+        assertEquals(parentInvoiceWithChildItems.getItems().size(), 3);
+        assertNull(parentInvoiceWithChildItems.getItems().get(0).getChildItems());
+        assertNull(parentInvoiceWithChildItems.getItems().get(1).getChildItems());
+        assertNull(parentInvoiceWithChildItems.getItems().get(2).getChildItems());
+
+        // check parent invoice without items but with child invoice items and adjustment. Should return items anyway.
+        // parameters: withItems = false, withChildrenItems = true
+        parentInvoiceWithChildItems = killBillClient.getInvoice(parentInvoices.get(1).getInvoiceId(), false, true, requestOptions);
+        assertEquals(parentInvoiceWithChildItems.getItems().size(), 3);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(0).getChildItems().size(), 2);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(1).getChildItems().size(), 1);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(2).getChildItems().size(), 1);
+
+    }
+
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
index cdf5c3d..9c418c8 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
@@ -241,17 +241,9 @@ public class TestPayment extends TestJaxrsBase {
         mockPaymentProviderPlugin.makeNextPaymentFailWithError();
         createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
 
-        HashMultimap<String, String> queryParams = HashMultimap.create();
-        queryParams.put("withAttempts", "true");
-        RequestOptions inputOptions = RequestOptions.builder()
-                                                    .withCreatedBy(createdBy)
-                                                    .withReason(reason)
-                                                    .withComment(comment)
-                                                    .withQueryParams(queryParams).build();
-
-        Payments payments = killBillClient.searchPayments("", 0L, 100L, AuditLevel.NONE, inputOptions);
+        Payments payments = killBillClient.searchPayments("", 0L, 100L, AuditLevel.NONE, requestOptions);
         Assert.assertNotNull(payments.get(0));
-        Payment payment = killBillClient.getPayment(payments.get(0).getPaymentId(), inputOptions);
+        Payment payment = killBillClient.getPayment(payments.get(0).getPaymentId(), false, true, ImmutableMap.<String, String>of(), AuditLevel.NONE, requestOptions);
 
         Assert.assertNotNull(payment.getPaymentAttempts());
         Assert.assertEquals(payment.getPaymentAttempts().get(0).getStateName(), "RETRIED");
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java b/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java
index 879f9f9..5724939 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java
@@ -195,10 +195,9 @@ public class PlanAligner extends BaseAligner {
                                                    final DateTime effectiveDate,
                                                    final InternalTenantContext context)
             throws CatalogApiException, SubscriptionBaseApiException {
-        final Catalog catalog = catalogService.getFullCatalog(context);
+        final Catalog catalog = catalogService.getFullCatalog(true, context);
 
         final PlanSpecifier planSpecifier = new PlanSpecifier(plan.getProduct().getName(),
-                                                              plan.getProduct().getCategory(),
                                                               plan.getRecurringBillingPeriod(),
                                                               priceList);
 
@@ -251,16 +250,14 @@ public class PlanAligner extends BaseAligner {
                                              final PhaseType originalInitialPhase,
                                              final WhichPhase which,
                                              final InternalTenantContext context) throws CatalogApiException, SubscriptionBaseApiException {
-        final Catalog catalog = catalogService.getFullCatalog(context);
+        final Catalog catalog = catalogService.getFullCatalog(true, context);
         final ProductCategory currentCategory = currentPlan.getProduct().getCategory();
         final PlanPhaseSpecifier fromPlanPhaseSpecifier = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
-                                                                                 currentCategory,
                                                                                  currentPlan.getRecurringBillingPeriod(),
                                                                                  currentPriceList,
                                                                                  currentPhase.getPhaseType());
 
         final PlanSpecifier toPlanSpecifier = new PlanSpecifier(nextPlan.getProduct().getName(),
-                                                                nextPlan.getProduct().getCategory(),
                                                                 nextPlan.getRecurringBillingPeriod(),
                                                                 priceList);
 
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionApiBase.java b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionApiBase.java
index e576d0e..e8f62ed 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionApiBase.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionApiBase.java
@@ -62,7 +62,7 @@ public class SubscriptionApiBase {
     protected DefaultSubscriptionBase createSubscriptionForApiUse(SubscriptionBuilder builder, List<SubscriptionBaseEvent> events, final InternalTenantContext context) throws CatalogApiException {
         final DefaultSubscriptionBase subscription = new DefaultSubscriptionBase(builder, apiService, clock);
         if (events.size() > 0) {
-            subscription.rebuildTransitions(events, catalogService.getFullCatalog(context));
+            subscription.rebuildTransitions(events, catalogService.getFullCatalog(true, context));
         }
         return subscription;
     }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index 2e7a534..77493b7 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -47,6 +47,7 @@ import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
@@ -155,10 +156,10 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
             */
 
             final CallContext callContext = internalCallContextFactory.createCallContext(context);
-            final Catalog catalog = catalogService.getFullCatalog(context);
+            final Catalog catalog = catalogService.getFullCatalog(true, context);
             final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(overrides, callContext);
 
-            final Plan plan = catalog.createOrFindPlan(spec.getProductName(), spec.getBillingPeriod(), realPriceList, overridesWithContext, effectiveDate);
+            final Plan plan = catalog.createOrFindPlan(new PlanSpecifier(spec.getProductName(), spec.getBillingPeriod(), realPriceList), overridesWithContext, effectiveDate);
             final PlanPhase phase = plan.getAllPhases()[0];
             if (phase == null) {
                 throw new SubscriptionBaseError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog",
@@ -193,9 +194,15 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
         try {
             final List<SubscriptionSpecifier> subscriptions = new ArrayList<SubscriptionSpecifier>();
-            final Catalog catalog = catalogService.getFullCatalog(context);
+            final Catalog catalog = catalogService.getFullCatalog(true, context);
             final CallContext callContext = internalCallContextFactory.createCallContext(context);
 
+            final SubscriptionBaseBundle bundle = dao.getSubscriptionBundleFromId(bundleId, context);
+            if (bundle == null) {
+                throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BUNDLE, bundleId);
+            }
+
+            boolean first = true;
             for (EntitlementSpecifier entitlement : entitlements) {
 
                 final PlanPhaseSpecifier spec = entitlement.getPlanPhaseSpecifier();
@@ -203,16 +210,18 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
                 final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(entitlement.getOverrides(), callContext);
 
-                final Plan plan = catalog.createOrFindPlan(spec.getProductName(), spec.getBillingPeriod(), realPriceList, overridesWithContext, effectiveDate);
+                final Plan plan = catalog.createOrFindPlan(new PlanSpecifier(spec.getProductName(), spec.getBillingPeriod(), realPriceList), overridesWithContext, effectiveDate);
                 final PlanPhase phase = plan.getAllPhases()[0];
                 if (phase == null) {
                     throw new SubscriptionBaseError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog",
                                                                   spec.getProductName(), spec.getBillingPeriod().toString(), realPriceList));
                 }
 
-                final SubscriptionBaseBundle bundle = dao.getSubscriptionBundleFromId(bundleId, context);
-                if (bundle == null) {
-                    throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BUNDLE, bundleId);
+                if (first) {
+                    first = false;
+                    if (plan.getProduct().getCategory() != ProductCategory.BASE) {
+                        throw new SubscriptionBaseApiException(new IllegalArgumentException(), ErrorCode.SUB_CREATE_NO_BP.getCode(), "Missing Base Subscription.");
+                    }
                 }
 
                 SubscriptionSpecifier subscription = new SubscriptionSpecifier();
@@ -531,11 +540,11 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
         try {
             final PlanPhaseSpecifier inputSpec = dryRunArguments.getPlanPhaseSpecifier();
             final String realPriceList = (inputSpec != null && inputSpec.getPriceListName() != null) ? inputSpec.getPriceListName() : PriceListSet.DEFAULT_PRICELIST_NAME;
-            final Catalog catalog = catalogService.getFullCatalog(context);
+            final Catalog catalog = catalogService.getFullCatalog(true, context);
 
             final PlanPhasePriceOverridesWithCallContext overridesWithContext = null; // TODO not supported to dryRun with custom price
             final Plan plan = (inputSpec != null && inputSpec.getProductName() != null && inputSpec.getBillingPeriod() != null) ?
-                              catalog.createOrFindPlan(inputSpec.getProductName(), inputSpec.getBillingPeriod(), realPriceList, overridesWithContext, utcNow) : null;
+                              catalog.createOrFindPlan(new PlanSpecifier(inputSpec.getProductName(), inputSpec.getBillingPeriod(), realPriceList), overridesWithContext, utcNow) : null;
             final TenantContext tenantContext = internalCallContextFactory.createTenantContext(context);
 
             if (dryRunArguments != null) {
@@ -585,11 +594,10 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
                                 final Plan currentPlan = subscriptionForCancellation.getCurrentPlan();
                                 final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
-                                                                                       currentPlan.getProduct().getCategory(),
                                                                                        subscriptionForCancellation.getCurrentPlan().getRecurringBillingPeriod(),
                                                                                        subscriptionForCancellation.getCurrentPriceList().getName(),
                                                                                        subscriptionForCancellation.getCurrentPhase().getPhaseType());
-                                policy = catalogService.getFullCatalog(context).planCancelPolicy(spec, utcNow);
+                                policy = catalogService.getFullCatalog(true, context).planCancelPolicy(spec, utcNow);
                             }
                             cancelEffectiveDate = subscriptionForCancellation.getPlanChangeEffectiveDate(policy);
                         }
@@ -665,7 +673,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
     public int getDefaultBillCycleDayLocal(final SubscriptionBase subscription, final SubscriptionBase baseSubscription, final PlanPhaseSpecifier planPhaseSpecifier, final DateTimeZone accountTimeZone, final int accountBillCycleDayLocal, final DateTime effectiveDate, final InternalTenantContext context) throws SubscriptionBaseApiException {
 
         try {
-            final Catalog catalog = catalogService.getFullCatalog(context);
+            final Catalog catalog = catalogService.getFullCatalog(true, context);
             final BillingAlignment alignment = catalog.billingAlignment(planPhaseSpecifier, effectiveDate);
             return BillCycleDayCalculator.calculateBcdForAlignment(subscription, baseSubscription, alignment, accountTimeZone, accountBillCycleDayLocal);
         } catch (final CatalogApiException e) {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java
index f554eeb..552f878 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java
@@ -124,7 +124,7 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
             final String planNameWithClosure = planName;
             final String planPhaseNameWithClosure = planPhaseName;
             final Integer billCycleDayLocalWithClosure = billCycleDayLocal;
-            final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceListName, phaseType);
+            final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, billingPeriod, priceListName, phaseType);
             result.add(new ExistingEvent() {
                 @Override
                 public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
@@ -132,6 +132,11 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
                 }
 
                 @Override
+                public ProductCategory getProductCategory() {
+                    return category;
+                }
+
+                @Override
                 public PlanPhaseSpecifier getPlanPhaseSpecifier() {
                     return spec;
                 }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimelineApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimelineApi.java
index 4dca0a1..49b48fc 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimelineApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimelineApi.java
@@ -136,7 +136,7 @@ public class DefaultSubscriptionBaseTimelineApi extends SubscriptionApiBase impl
 
         for (final SubscriptionBase cur : subscriptions) {
             if (!repairIds.contains(cur.getId())) {
-                result.add(new DefaultSubscriptionBaseTimeline((DefaultSubscriptionBase) cur, catalogService.getFullCatalog(tenantContext)));
+                result.add(new DefaultSubscriptionBaseTimeline((DefaultSubscriptionBase) cur, catalogService.getFullCatalog(true, tenantContext)));
             }
         }
         return result;
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
index e7ec71a..0443fd7 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
@@ -83,7 +83,7 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
 
         SubscriptionBaseEvent newEvent = null;
 
-        final Catalog catalog = catalogService.getFullCatalog(context);
+        final Catalog catalog = catalogService.getFullCatalog(true, context);
 
         final DateTime effectiveDate = existingEvent.getEffectiveDate().isBefore(transferDate) ? transferDate : existingEvent.getEffectiveDate();
 
@@ -215,7 +215,7 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
                     continue;
                 }
                 final List<ExistingEvent> existingEvents = cur.getExistingEvents();
-                final ProductCategory productCategory = existingEvents.get(0).getPlanPhaseSpecifier().getProductCategory();
+                final ProductCategory productCategory = existingEvents.get(0).getProductCategory();
 
                 // For future add-on cancellations, don't add a cancellation on disk right away (mirror the behavior
                 // on base plan cancellations, even though we don't support un-transfer today)
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
index bab81c5..ce19dd3 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
@@ -110,7 +110,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
             final List<SubscriptionBaseEvent> events = getEventsOnCreation(subscription.getBundleId(), subscription.getId(), subscription.getAlignStartDate(), subscription.getBundleStartDate(),
                                                                            plan, initialPhase, realPriceList, effectiveDate, processedDate, internalCallContext);
             dao.createSubscription(subscription, events, internalCallContext);
-            subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), catalogService.getFullCatalog(internalCallContext));
+            subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), catalogService.getFullCatalog(true, internalCallContext));
             return subscription;
         } catch (final CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
@@ -145,7 +145,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         final DefaultSubscriptionBase baseSubscription = findBaseSubscription(subscriptionBaseList);
         try {
             baseSubscription.rebuildTransitions(dao.getEventsForSubscription(baseSubscription.getId(), internalCallContext),
-                                                catalogService.getFullCatalog(internalCallContext));
+                                                catalogService.getFullCatalog(true, internalCallContext));
 
             for (final DefaultSubscriptionBase input : subscriptionBaseList) {
                 if (input.getId().equals(baseSubscription.getId())) {
@@ -153,7 +153,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                 }
 
                 input.rebuildTransitions(dao.getEventsForSubscription(input.getId(), internalCallContext),
-                                         catalogService.getFullCatalog(internalCallContext));
+                                         catalogService.getFullCatalog(true, internalCallContext));
             }
         } catch (CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
@@ -180,14 +180,12 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
         final Plan currentPlan = subscription.getCurrentPlan();
         final PlanPhaseSpecifier planPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
-                                                                    currentPlan.getProduct().getCategory(),
                                                                     subscription.getCurrentPlan().getRecurringBillingPeriod(),
-                                                                    subscription.getCurrentPriceList().getName(),
-                                                                    subscription.getCurrentPhase().getPhaseType());
+                                                                    subscription.getCurrentPriceList().getName(), null);
 
         try {
             final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
-            final BillingActionPolicy policy = catalogService.getFullCatalog(internalCallContext).planCancelPolicy(planPhase, now);
+            final BillingActionPolicy policy = catalogService.getFullCatalog(true, internalCallContext).planCancelPolicy(planPhase, now);
             final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy);
 
             return doCancelPlan(ImmutableMap.<DefaultSubscriptionBase, DateTime>of(subscription, effectiveDate), now, internalCallContext);
@@ -254,7 +252,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
             boolean allSubscriptionsCancelled = true;
             for (final DefaultSubscriptionBase subscription : subscriptions.keySet()) {
-                final Catalog fullCatalog = catalogService.getFullCatalog(internalCallContext);
+                final Catalog fullCatalog = catalogService.getFullCatalog(true, internalCallContext);
                 subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), fullCatalog);
                 allSubscriptionsCancelled = allSubscriptionsCancelled && (subscription.getState() == EntitlementState.CANCELLED);
             }
@@ -291,7 +289,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
         dao.uncancelSubscription(subscription, uncancelEvents, internalCallContext);
         try {
-            final Catalog fullCatalog = catalogService.getFullCatalog(internalCallContext);
+            final Catalog fullCatalog = catalogService.getFullCatalog(true, internalCallContext);
             subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), fullCatalog);
             return true;
         } catch (final CatalogApiException e) {
@@ -382,20 +380,18 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         final PlanChangeResult planChangeResult;
         try {
             final InternalTenantContext internalCallContext = createTenantContextFromBundleId(subscription.getBundleId(), context);
-            final Product destProduct = catalogService.getFullCatalog(internalCallContext).findProduct(productName, effectiveDate);
+            final Product destProduct = catalogService.getFullCatalog(true, internalCallContext).findProduct(productName, effectiveDate);
             final Plan currentPlan = subscription.getCurrentPlan();
             final PriceList currentPriceList = subscription.getCurrentPriceList();
             final PlanPhaseSpecifier fromPlanPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
-                                                                            currentPlan.getProduct().getCategory(),
                                                                             currentPlan.getRecurringBillingPeriod(),
                                                                             currentPriceList.getName(),
                                                                             subscription.getCurrentPhase().getPhaseType());
             final PlanSpecifier toPlanPhase = new PlanSpecifier(productName,
-                                                                destProduct.getCategory(),
                                                                 term,
                                                                 priceList);
 
-            planChangeResult = catalogService.getFullCatalog(internalCallContext).planChange(fromPlanPhase, toPlanPhase, effectiveDate);
+            planChangeResult = catalogService.getFullCatalog(true, internalCallContext).planChange(fromPlanPhase, toPlanPhase, effectiveDate);
         } catch (final CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
@@ -412,7 +408,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                               final CallContext context) throws SubscriptionBaseApiException, CatalogApiException {
         final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
         final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(overrides, context);
-        final Plan newPlan = catalogService.getFullCatalog(internalCallContext).createOrFindPlan(newProductName, newBillingPeriod, newPriceList, overridesWithContext, effectiveDate, subscription.getStartDate());
+        final Plan newPlan = catalogService.getFullCatalog(true, internalCallContext).createOrFindPlan(new PlanSpecifier(newProductName, newBillingPeriod, newPriceList), overridesWithContext, effectiveDate, subscription.getStartDate());
 
         if (newPlan.getProduct().getCategory() != subscription.getCategory()) {
             throw new SubscriptionBaseApiException(ErrorCode.SUB_CHANGE_INVALID, subscription.getId());
@@ -424,7 +420,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
         dao.changePlan(subscription, changeEvents, addOnSubscriptionsToBeCancelled, addOnCancelEvents, internalCallContext);
 
-        final Catalog fullCatalog = catalogService.getFullCatalog(internalCallContext);
+        final Catalog fullCatalog = catalogService.getFullCatalog(true, internalCallContext);
         subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), fullCatalog);
     }
 
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java
index 6d5a679..dbac2cd 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java
@@ -46,7 +46,7 @@ public class AddonUtils {
             throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_BP_NON_ACTIVE, targetAddOnPlan.getName());
         }
 
-        final Product baseProduct = catalogService.getFullCatalog(context).findProduct(baseSubscription.getCurrentPlan().getProduct().getName(), requestedDate);
+        final Product baseProduct = catalogService.getFullCatalog(true, context).findProduct(baseSubscription.getCurrentPlan().getProduct().getName(), requestedDate);
         if (isAddonIncluded(baseProduct, targetAddOnPlan)) {
             throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_ALREADY_INCLUDED,
                                                    targetAddOnPlan.getName(), baseSubscription.getCurrentPlan().getProduct().getName());
@@ -60,7 +60,7 @@ public class AddonUtils {
 
     public boolean isAddonAvailableFromProdName(final String baseProductName, final Plan targetAddOnPlan, final DateTime requestedDate, final InternalTenantContext context) {
         try {
-            final Product product = catalogService.getFullCatalog(context).findProduct(baseProductName, requestedDate);
+            final Product product = catalogService.getFullCatalog(true, context).findProduct(baseProductName, requestedDate);
             return isAddonAvailable(product, targetAddOnPlan);
         } catch (CatalogApiException e) {
             throw new SubscriptionBaseError(e);
@@ -69,7 +69,7 @@ public class AddonUtils {
 
     public boolean isAddonAvailableFromPlanName(final String basePlanName, final Plan targetAddOnPlan, final DateTime requestedDate, final InternalTenantContext context) {
         try {
-            final Plan plan = catalogService.getFullCatalog(context).findPlan(basePlanName, requestedDate);
+            final Plan plan = catalogService.getFullCatalog(true, context).findPlan(basePlanName, requestedDate);
             final Product product = plan.getProduct();
             return isAddonAvailable(product, targetAddOnPlan);
         } catch (CatalogApiException e) {
@@ -80,7 +80,7 @@ public class AddonUtils {
 
     public boolean isAddonIncludedFromProdName(final String baseProductName, final Plan targetAddOnPlan, final DateTime requestedDate, final InternalTenantContext context) {
         try {
-            final Product product = catalogService.getFullCatalog(context).findProduct(baseProductName, requestedDate);
+            final Product product = catalogService.getFullCatalog(true, context).findProduct(baseProductName, requestedDate);
             return isAddonIncluded(product, targetAddOnPlan);
         } catch (CatalogApiException e) {
             throw new SubscriptionBaseError(e);
@@ -90,7 +90,7 @@ public class AddonUtils {
 
     public boolean isAddonIncludedFromPlanName(final String basePlanName, final Plan targetAddOnPlan, final DateTime requestedDate, final InternalTenantContext context) {
         try {
-            final Plan plan = catalogService.getFullCatalog(context).findPlan(basePlanName, requestedDate);
+            final Plan plan = catalogService.getFullCatalog(true, context).findPlan(basePlanName, requestedDate);
             final Product product = plan.getProduct();
             return isAddonIncluded(product, targetAddOnPlan);
         } catch (CatalogApiException e) {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 9b5b400..730f5cb 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -940,7 +940,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
     private DefaultSubscriptionBase createSubscriptionForInternalUse(final SubscriptionBase shellSubscription, final List<SubscriptionBaseEvent> events, final InternalTenantContext context) throws CatalogApiException {
         final DefaultSubscriptionBase result = new DefaultSubscriptionBase(new SubscriptionBuilder(((DefaultSubscriptionBase) shellSubscription)), null, clock);
         if (events.size() > 0) {
-            final Catalog fullCatalog = catalogService.getFullCatalog(context);
+            final Catalog fullCatalog = catalogService.getFullCatalog(true, context);
             result.rebuildTransitions(events, fullCatalog);
         }
         return result;
@@ -1064,7 +1064,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
             allEvents.addAll(subscriptionWithNewEvent.getEvents());
         }
         allEvents.add(newEvent);
-        subscriptionWithNewEvent.rebuildTransitions(allEvents, catalogService.getFullCatalog(context));
+        subscriptionWithNewEvent.rebuildTransitions(allEvents, catalogService.getFullCatalog(true, context));
         return subscriptionWithNewEvent;
     }
 
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java b/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java
index 513ddad..bd1a180 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java
@@ -159,7 +159,7 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
                                                                     phaseType,
                                                                     ApiEventType.CREATE
                                                                    );
-        defaultSubscriptionBase.rebuildTransitions(ImmutableList.<SubscriptionBaseEvent>of(event), catalogService.getFullCatalog(internalCallContext));
+        defaultSubscriptionBase.rebuildTransitions(ImmutableList.<SubscriptionBaseEvent>of(event), catalogService.getFullCatalog(true, internalCallContext));
 
         Assert.assertEquals(defaultSubscriptionBase.getAllTransitions().size(), 1);
         Assert.assertNull(defaultSubscriptionBase.getAllTransitions().get(0).getPreviousPhase());
@@ -184,7 +184,7 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
                                                                     ApiEventType.CHANGE
                                                                    );
 
-        defaultSubscriptionBase.rebuildTransitions(ImmutableList.<SubscriptionBaseEvent>of(previousEvent, event), catalogService.getFullCatalog(internalCallContext));
+        defaultSubscriptionBase.rebuildTransitions(ImmutableList.<SubscriptionBaseEvent>of(previousEvent, event), catalogService.getFullCatalog(true, internalCallContext));
 
         final List<SubscriptionBaseTransition> newTransitions = defaultSubscriptionBase.getAllTransitions();
         Assert.assertEquals(newTransitions.size(), 2);
@@ -213,7 +213,7 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
                                                  final String newProductName,
                                                  final DateTime effectiveChangeDate) throws CatalogApiException, SubscriptionBaseApiException {
         // The date is used for different catalog versions - we don't care here
-        final Plan newPlan = catalogService.getFullCatalog(internalCallContext).findPlan(newProductName, clock.getUTCNow());
+        final Plan newPlan = catalogService.getFullCatalog(true, internalCallContext).findPlan(newProductName, clock.getUTCNow());
 
         return planAligner.getNextTimedPhaseOnChange(defaultSubscriptionBase, newPlan, priceList, effectiveChangeDate, internalCallContext);
     }
@@ -223,7 +223,7 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
                                                 final DefaultSubscriptionBase defaultSubscriptionBase,
                                                 final DateTime effectiveDate) throws CatalogApiException, SubscriptionBaseApiException {
         // The date is used for different catalog versions - we don't care here
-        final Plan plan = catalogService.getFullCatalog(internalCallContext).findPlan(productName, clock.getUTCNow());
+        final Plan plan = catalogService.getFullCatalog(true, internalCallContext).findPlan(productName, clock.getUTCNow());
 
         // Same here for the requested date
         final TimedPhase[] phases = planAligner.getCurrentAndNextTimedPhaseOnCreate(defaultSubscriptionBase.getAlignStartDate(), defaultSubscriptionBase.getBundleStartDate(),
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
index c6704e1..466b2e2 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
@@ -134,7 +134,7 @@ public class TestDefaultSubscriptionTransferApi extends SubscriptionTestSuiteNoD
             @Override
             public PlanPhaseSpecifier getPlanPhaseSpecifier() {
                 return SubscriptionBaseTransitionType.CANCEL.equals(subscriptionTransitionType) ? null :
-                       new PlanPhaseSpecifier("BicycleTrialEvergreen1USD", ProductCategory.BASE, BillingPeriod.NO_BILLING_PERIOD,
+                       new PlanPhaseSpecifier("BicycleTrialEvergreen1USD",  BillingPeriod.NO_BILLING_PERIOD,
                                               PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.FIXEDTERM);
             }
 
@@ -142,6 +142,11 @@ public class TestDefaultSubscriptionTransferApi extends SubscriptionTestSuiteNoD
             public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
                 return subscriptionTransitionType;
             }
+
+            @Override
+            public ProductCategory getProductCategory() {
+                return ProductCategory.BASE;
+            }
         };
     }
 }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
index d93ee18..52ba58e 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
@@ -91,7 +91,7 @@ public class TestSubscriptionHelper {
             throws SubscriptionBaseApiException {
         testListener.pushExpectedEvent(NextEvent.CREATE);
         final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionApi.createSubscription(bundleId,
-                                                                                                                  new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSet, null), null,
+                                                                                                                  new PlanPhaseSpecifier(productName, term, planSet, null), null,
                                                                                                                   requestedDate == null ? clock.getUTCNow() : requestedDate, false, internalCallContext);
         assertNotNull(subscription);
 
@@ -162,7 +162,7 @@ public class TestSubscriptionHelper {
     public PlanPhaseSpecifier getProductSpecifier(final String productName, final String priceList,
                                                   final BillingPeriod term,
                                                   @Nullable final PhaseType phaseType) {
-        return new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, priceList, phaseType);
+        return new PlanPhaseSpecifier(productName, term, priceList, phaseType);
     }
 
     public void printEvents(final List<SubscriptionBaseEvent> events) {
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiAddOn.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiAddOn.java
index 8829276..424475e 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiAddOn.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiAddOn.java
@@ -396,7 +396,6 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
 
         // This is just to double check our test catalog gives us what we want before we start the test
         final PlanSpecifier planSpecifier = new PlanSpecifier(aoProduct,
-                                                              ProductCategory.ADD_ON,
                                                               aoTerm,
                                                               aoPriceList);
         final PlanAlignmentCreate alignement = catalog.planCreateAlignment(planSpecifier, clock.getUTCNow());
@@ -413,7 +412,6 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
 
         // This is just to double check our test catalog gives us what we want before we start the test
         final PlanSpecifier planSpecifier = new PlanSpecifier(aoProduct,
-                                                              ProductCategory.ADD_ON,
                                                               aoTerm,
                                                               aoPriceList);
         final PlanAlignmentCreate alignement = catalog.planCreateAlignment(planSpecifier, clock.getUTCNow());
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java b/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java
index d723dad..1fd582a 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java
@@ -55,7 +55,7 @@ public class DefaultSubscriptionTestInitializer implements SubscriptionTestIniti
     public Catalog initCatalog(final CatalogService catalogService, final InternalTenantContext context) throws Exception {
 
         ((DefaultCatalogService) catalogService).loadCatalog();
-        final Catalog catalog = catalogService.getFullCatalog(context);
+        final Catalog catalog = catalogService.getFullCatalog(true, context);
         assertNotNull(catalog);
         return catalog;
     }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
index 38500c3..d68e122 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
@@ -311,7 +311,7 @@ public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBun
         final DefaultSubscriptionBase subscription = new DefaultSubscriptionBase(new SubscriptionBuilder(in), null, clock);
         if (events.size() > 0) {
             try {
-                subscription.rebuildTransitions(getEventsForSubscription(in.getId(), context), catalogService.getFullCatalog(context));
+                subscription.rebuildTransitions(getEventsForSubscription(in.getId(), context), catalogService.getFullCatalog(true, context));
             } catch (final CatalogApiException e) {
                 log.warn("Failed to rebuild subscription", e);
             }
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantInternalApi.java b/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantInternalApi.java
index 7fb088e..fcf5f05 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantInternalApi.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantInternalApi.java
@@ -79,13 +79,13 @@ public class DefaultTenantInternalApi implements TenantInternalApi {
 
     @Override
     public String getInvoiceTemplate(final Locale locale, final InternalTenantContext tenantContext) {
-        final List<String> values = tenantDao.getTenantValueForKey(LocaleUtils.localeString(locale, TenantKey.INVOICE_TEMPLATE.toString()), tenantContext);
+        final List<String> values = tenantDao.getTenantValueForKey(TenantKey.INVOICE_TEMPLATE.toString(), tenantContext);
         return getUniqueValue(values, "invoice template", tenantContext);
     }
 
     @Override
     public String getManualPayInvoiceTemplate(final Locale locale, final InternalTenantContext tenantContext) {
-        final List<String> values = tenantDao.getTenantValueForKey(LocaleUtils.localeString(locale, TenantKey.INVOICE_MP_TEMPLATE.toString()), tenantContext);
+        final List<String> values = tenantDao.getTenantValueForKey(TenantKey.INVOICE_MP_TEMPLATE.toString(), tenantContext);
         return getUniqueValue(values, "manual pay invoice template", tenantContext);
     }
 
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java b/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java
index 5fec9ea..1fb59d8 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java
@@ -130,6 +130,15 @@ public class DefaultTenantUserApi implements TenantUserApi {
     }
 
     @Override
+    public void updateTenantKeyValue(final String key, final String value, final CallContext context) throws TenantApiException {
+        // Invalidate tenantKVCache after we store (to avoid race conditions). Multi-node invalidation will follow the TenantBroadcast pattern
+        final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context);
+        final String tenantKey = getCacheKeyName(key, internalContext);
+        tenantDao.updateTenantLastKeyValue(key, value, internalContext);
+        tenantKVCache.remove(tenantKey);
+    }
+
+    @Override
     public void deleteTenantKey(final String key, final CallContext context) throws TenantApiException {
         // Invalidate tenantKVCache after we delete (to avoid race conditions). Multi-node invalidation will follow the TenantBroadcast pattern
         final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context);
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java
index fa33b37..0b6ea7d 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java
@@ -154,6 +154,32 @@ public class DefaultTenantDao extends EntityDaoBase<TenantModelDao, Tenant, Tena
     }
 
     @Override
+    public void updateTenantLastKeyValue(final String key, final String value, final InternalCallContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                final TenantKVModelDao tenantKVModelDao = new TenantKVModelDao(UUIDs.randomUUID(), context.getCreatedDate(), context.getUpdatedDate(), key, value);
+                final TenantKVSqlDao tenantKVSqlDao = entitySqlDaoWrapperFactory.become(TenantKVSqlDao.class);
+
+                // Retrieve all values for key ordered with recordId (last at the end)
+                final List<TenantKVModelDao> tenantKV = tenantKVSqlDao.getTenantValueForKey(key, context);
+                final String id;
+                if (!tenantKV.isEmpty()) {
+                    id = tenantKV.get(tenantKV.size() - 1).getId().toString();
+                    tenantKVSqlDao.updateTenantValueKey(id, value, context);
+                } else {
+                    id = tenantKVModelDao.getId().toString();
+                    tenantKVSqlDao.create(tenantKVModelDao, context);
+                }
+                final TenantKVModelDao rehydrated = tenantKVSqlDao.getById(id, context);
+                broadcastConfigurationChangeFromTransaction(rehydrated.getRecordId(), key, entitySqlDaoWrapperFactory, context);
+                return null;
+            }
+        });
+
+    }
+
+    @Override
     public void deleteTenantKey(final String key, final InternalCallContext context) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantDao.java
index 3913ae2..c665cda 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantDao.java
@@ -110,6 +110,11 @@ public class NoCachingTenantDao extends EntityDaoBase<TenantModelDao, Tenant, Te
     }
 
     @Override
+    public void updateTenantLastKeyValue(final String key, final String value, final InternalCallContext context) {
+        throw new IllegalStateException("Not implemented by NoCachingTenantDao");
+    }
+
+    @Override
     public void deleteTenantKey(final String key, final InternalCallContext context) {
         throw new IllegalStateException("Not implemented by NoCachingTenantDao");
     }
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantDao.java
index 8f48adc..d52b1b3 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantDao.java
@@ -32,6 +32,8 @@ public interface TenantDao extends EntityDao<TenantModelDao, Tenant, TenantApiEx
 
     public void addTenantKeyValue(final String key, final String value, final boolean uniqueKey, final InternalCallContext context);
 
+    public void updateTenantLastKeyValue(final String key, final String value, final InternalCallContext context);
+
     public void deleteTenantKey(final String key, final InternalCallContext context);
 
     public TenantKVModelDao getKeyByRecordId(Long recordId, InternalTenantContext context);
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantKVSqlDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantKVSqlDao.java
index bdfed51..9e263cf 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantKVSqlDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantKVSqlDao.java
@@ -18,18 +18,17 @@ package org.killbill.billing.tenant.dao;
 
 import java.util.List;
 
-import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.BindBean;
-import org.skife.jdbi.v2.sqlobject.SqlQuery;
-import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-
-import org.killbill.billing.tenant.api.TenantKV;
-import org.killbill.billing.util.audit.ChangeType;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.tenant.api.TenantKV;
+import org.killbill.billing.util.audit.ChangeType;
 import org.killbill.billing.util.entity.dao.Audited;
 import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
 @EntitySqlDaoStringTemplate
 public interface TenantKVSqlDao extends EntitySqlDao<TenantKVModelDao, TenantKV> {
@@ -42,4 +41,12 @@ public interface TenantKVSqlDao extends EntitySqlDao<TenantKVModelDao, TenantKV>
     @Audited(ChangeType.DELETE)
     public void markTenantKeyAsDeleted(@Bind("id")final String id,
                                        @BindBean final InternalCallContext context);
+
+    @SqlUpdate
+    @Audited(ChangeType.UPDATE)
+    public void updateTenantValueKey(@Bind("id") final String id,
+                                     @Bind("tenantValue") final String tenantValue,
+                                     @BindBean final InternalCallContext context);
+
+
 }
diff --git a/tenant/src/main/resources/org/killbill/billing/tenant/dao/TenantKVSqlDao.sql.stg b/tenant/src/main/resources/org/killbill/billing/tenant/dao/TenantKVSqlDao.sql.stg
index 2895b30..c6aa736 100644
--- a/tenant/src/main/resources/org/killbill/billing/tenant/dao/TenantKVSqlDao.sql.stg
+++ b/tenant/src/main/resources/org/killbill/billing/tenant/dao/TenantKVSqlDao.sql.stg
@@ -36,6 +36,7 @@ from <tableName()> t
 where t.tenant_key = :tenantKey
 and  t.is_active
 <AND_CHECK_TENANT("t.")>
+<defaultOrderBy("t.")>
 ;
 >>
 
@@ -46,3 +47,12 @@ where id = :id
 <AND_CHECK_TENANT("")>
 ;
 >>
+
+
+updateTenantValueKey() ::= <<
+update <tableName()>
+set tenant_value = :tenantValue
+where id = :id
+<AND_CHECK_TENANT("")>
+;
+>>
\ No newline at end of file
diff --git a/tenant/src/test/java/org/killbill/billing/tenant/dao/TestDefaultTenantDao.java b/tenant/src/test/java/org/killbill/billing/tenant/dao/TestDefaultTenantDao.java
index 112e3ba..5b56007 100644
--- a/tenant/src/test/java/org/killbill/billing/tenant/dao/TestDefaultTenantDao.java
+++ b/tenant/src/test/java/org/killbill/billing/tenant/dao/TestDefaultTenantDao.java
@@ -75,4 +75,31 @@ public class TestDefaultTenantDao extends TenantTestSuiteWithEmbeddedDb {
         value = tenantDao.getTenantValueForKey("THE_KEY", internalCallContext);
         Assert.assertEquals(value.size(), 0);
     }
+
+
+
+
+    @Test(groups = "slow")
+    public void testTenantKeyValueUpdate() throws Exception {
+        final DefaultTenant tenant = new DefaultTenant(UUID.randomUUID(), null, null, UUID.randomUUID().toString(),
+                                                       UUID.randomUUID().toString(), UUID.randomUUID().toString());
+        tenantDao.create(new TenantModelDao(tenant), internalCallContext);
+
+        tenantDao .addTenantKeyValue("MY_KEY", "TheValue1", false, internalCallContext);
+        tenantDao .addTenantKeyValue("MY_KEY", "TheValue2", false, internalCallContext);
+        tenantDao .addTenantKeyValue("MY_KEY", "TheValue3", false, internalCallContext);
+
+        final List<String> value = tenantDao.getTenantValueForKey("MY_KEY", internalCallContext);
+        Assert.assertEquals(value.size(), 3);
+
+
+        tenantDao.updateTenantLastKeyValue("MY_KEY", "NewValue3", internalCallContext);
+
+        final List<String> newValues = tenantDao.getTenantValueForKey("MY_KEY", internalCallContext);
+        Assert.assertEquals(newValues.size(), 3);
+
+        Assert.assertEquals(newValues.get(0), "TheValue1");
+        Assert.assertEquals(newValues.get(1), "TheValue2");
+        Assert.assertEquals(newValues.get(2), "NewValue3");
+    }
 }