killbill-aplcache

Basic implementation for catalog price override (some hacks

3/25/2015 6:52:40 PM

Changes

Details

diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultFixed.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultFixed.java
index 8480115..d3d525a 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultFixed.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultFixed.java
@@ -27,6 +27,7 @@ import org.killbill.billing.catalog.api.Fixed;
 import org.killbill.billing.catalog.api.FixedType;
 import org.killbill.billing.catalog.api.InternationalPrice;
 import org.killbill.billing.catalog.api.PlanPhase;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.xmlloader.ValidatingConfig;
 import org.killbill.xmlloader.ValidationErrors;
 
@@ -54,6 +55,13 @@ public class DefaultFixed extends ValidatingConfig<StandaloneCatalog> implements
     }
 
 
+    public DefaultFixed() {}
+
+    public DefaultFixed(final DefaultFixed in, final PlanPhasePriceOverride override) {
+        this.type = in.getType();
+        this.fixedPrice = new DefaultInternationalPrice(fixedPrice, override, true);
+    }
+
     @Override
     public void initialize(final StandaloneCatalog root, final URI uri) {
         if (fixedPrice != null) {
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultInternationalPrice.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultInternationalPrice.java
index 2607cfd..0cea252 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultInternationalPrice.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultInternationalPrice.java
@@ -27,6 +27,7 @@ import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.CurrencyValueNull;
 import org.killbill.billing.catalog.api.InternationalPrice;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.Price;
 import org.killbill.xmlloader.ValidatingConfig;
 import org.killbill.xmlloader.ValidationErrors;
@@ -48,8 +49,20 @@ public class DefaultInternationalPrice extends ValidatingConfig<StandaloneCatalo
         return prices;
     }
 
-
-
+    public DefaultInternationalPrice() {}
+
+    public DefaultInternationalPrice(final DefaultInternationalPrice in, final PlanPhasePriceOverride override, final boolean fixed) {
+        this.prices = new DefaultPrice[in.getPrices().length];
+        // There is a question on whether we keep the other prices that were not overridden or only have one entry for the overridden price on that currency.
+        for (int i = 0; i < in.getPrices().length; i++) {
+            final DefaultPrice curPrice = (DefaultPrice)  in.getPrices()[i];
+            if (curPrice.getCurrency().equals(override.getCurrency())) {
+                prices[i] = new DefaultPrice(fixed ? override.getFixedPrice() : override.getRecurringPrice(), override.getCurrency());
+            } else {
+                prices[i] = curPrice;
+            }
+        }
+    }
 
     /* (non-Javadoc)
       * @see org.killbill.billing.catalog.IInternationalPrice#getPrice(org.killbill.billing.catalog.api.Currency)
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 505414c..7770d74 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.Iterator;
+import java.util.List;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -38,6 +39,7 @@ 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.PlanPhase;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.Recurring;
 import org.killbill.xmlloader.ValidatingConfig;
@@ -76,6 +78,22 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
     @XmlElement(required = false)
     private Integer plansAllowedInBundle = 1;
 
+
+    public DefaultPlan() {}
+
+    public DefaultPlan(final String planName, final DefaultPlan in, final PlanPhasePriceOverride[] overrides) {
+        this.name = planName;
+        this.retired = in.isRetired();
+        this.effectiveDateForExistingSubscriptons = in.getEffectiveDateForExistingSubscriptons();
+        this.product = (DefaultProduct) in.getProduct();
+        this.initialPhases = new DefaultPlanPhase[in.getInitialPhases().length];
+        for (int i = 0; i< overrides.length; i++) {
+            final DefaultPlanPhase newPhase = new DefaultPlanPhase(in.getFinalPhase(), overrides[i]);
+            initialPhases[i] = newPhase;
+        }
+        this.finalPhase = new DefaultPlanPhase(in.getFinalPhase(), overrides[overrides.length - 1]);
+
+    }
     /* (non-Javadoc)
       * @see org.killbill.billing.catalog.IPlan#getEffectiveDateForExistingSubscriptons()
       */
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java
index ec69d44..77971a8 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java
@@ -20,6 +20,7 @@ package org.killbill.billing.catalog;
 
 import java.net.URI;
 
+import javax.annotation.Nullable;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
@@ -33,6 +34,7 @@ import org.killbill.billing.catalog.api.Fixed;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.Recurring;
 import org.killbill.billing.catalog.api.Usage;
 import org.killbill.xmlloader.ValidatingConfig;
@@ -61,6 +63,20 @@ public class DefaultPlanPhase extends ValidatingConfig<StandaloneCatalog> implem
     //Not exposed in XML
     private Plan plan;
 
+    public DefaultPlanPhase() {}
+
+    public DefaultPlanPhase(final DefaultPlanPhase in, @Nullable final PlanPhasePriceOverride override) {
+        this.type = in.getPhaseType();
+        this.duration = (DefaultDuration) in.getDuration();
+        this.fixed = override != null && override.getFixedPrice() != null ? new DefaultFixed((DefaultFixed) in.getFixed(), override) : (DefaultFixed) in.getFixed();
+        this.recurring = override != null && override.getRecurringPrice() != null ? new DefaultRecurring((DefaultRecurring) in.getRecurring(), override) : (DefaultRecurring) in.getRecurring();
+        this.usages = new DefaultUsage[in.getUsages().length];
+        for (int i = 0; i < in.getUsages().length; i++) {
+            usages[i] = (DefaultUsage) in.getUsages()[i];
+        }
+        this.plan = in.plan;
+    }
+
     public static String phaseName(final String planName, final PhaseType phasetype) {
         return planName + "-" + phasetype.toString().toLowerCase();
     }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultRecurring.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultRecurring.java
index 644052e..b28f371 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultRecurring.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultRecurring.java
@@ -26,6 +26,7 @@ import javax.xml.bind.annotation.XmlElement;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.Recurring;
 import org.killbill.xmlloader.ValidatingConfig;
 import org.killbill.xmlloader.ValidationError;
@@ -44,6 +45,13 @@ public class DefaultRecurring extends ValidatingConfig<StandaloneCatalog> implem
     private Plan plan;
     private PlanPhase phase;
 
+    public DefaultRecurring() {};
+
+    public DefaultRecurring(final DefaultRecurring in, final PlanPhasePriceOverride override) {
+        this.billingPeriod = in.getBillingPeriod();
+        this.recurringPrice = new DefaultInternationalPrice(recurringPrice, override, false);
+    }
+
     @Override
     public BillingPeriod getBillingPeriod() {
         return billingPeriod;
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
index 66501d1..bf4c557 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
@@ -24,19 +24,19 @@ import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
 
-import com.google.common.io.Resources;
-import com.google.inject.Inject;
-
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.override.PriceOverride;
-import org.killbill.billing.platform.api.KillbillService.ServiceException;
 import org.killbill.clock.Clock;
 import org.killbill.xmlloader.UriAccessor;
 import org.killbill.xmlloader.XMLLoader;
 
+import com.google.common.io.Resources;
+import com.google.inject.Inject;
+
 public class VersionedCatalogLoader implements CatalogLoader {
 
     private static final Object PROTOCOL_FOR_FILE = "file";
@@ -82,7 +82,7 @@ public class VersionedCatalogLoader implements CatalogLoader {
             final VersionedCatalog result = new VersionedCatalog(clock);
             for (final URI u : xmlURIs) {
                 final StandaloneCatalog catalog = XMLLoader.getObjectFromUri(u, StandaloneCatalog.class);
-                result.add(catalog);
+                result.add(new StandaloneCatalogWithPriceOverride(catalog, priceOverride));
             }
             return result;
         } catch (Exception e) {
@@ -98,8 +98,7 @@ public class VersionedCatalogLoader implements CatalogLoader {
             for (final String cur : catalogXMLs) {
                 final InputStream curCatalogStream = new ByteArrayInputStream(cur.getBytes());
                 final StandaloneCatalog catalog = XMLLoader.getObjectFromStream(uri, curCatalogStream, StandaloneCatalog.class);
-                catalog.setPriceOverride(priceOverride);
-                result.add(catalog);
+                result.add(new StandaloneCatalogWithPriceOverride(catalog, priceOverride));
             }
             return result;
         } catch (Exception e) {
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
index c5415f2..6dcb2e1 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
@@ -19,21 +19,60 @@ package org.killbill.billing.catalog.override;
 
 import java.util.List;
 
+import org.joda.time.DateTime;
+import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.DefaultPlan;
+import org.killbill.billing.catalog.DefaultPlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.dao.CatalogOverrideDao;
+import org.killbill.billing.catalog.dao.CatalogOverridePlanDefinitionModelDao;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 
 public class DefaultPriceOverride implements PriceOverride {
 
+    private final CatalogOverrideDao overrideDao;
+
     @Inject
-    public DefaultPriceOverride() {
+    public DefaultPriceOverride(final CatalogOverrideDao overrideDao) {
+        this.overrideDao = overrideDao;
     }
 
     @Override
-    public DefaultPlan getOverriddenPlan(final Plan parentPlan, final List<PlanPhasePriceOverride> overrides) throws CatalogApiException {
-        return null;
+    public DefaultPlan getOrCreateOverriddenPlan(final Plan parentPlan, final DateTime catalogEffectiveDate, final List<PlanPhasePriceOverride> overrides, final InternalCallContext context) throws CatalogApiException {
+
+        final PlanPhasePriceOverride[] resolvedOverride = new PlanPhasePriceOverride[parentPlan.getAllPhases().length];
+        int index  = 0;
+        for (final PlanPhase curPhase : parentPlan.getAllPhases()) {
+            final PlanPhasePriceOverride curOverride = Iterables.tryFind(overrides, new Predicate<PlanPhasePriceOverride>() {
+                @Override
+                public boolean apply(final PlanPhasePriceOverride input) {
+                    if (input.getPhaseName() != null && input.getPhaseName().equals(curPhase.getName())) {
+                        return true;
+                    }
+                    // If the phaseName was not passed, we infer by matching the phaseType. This obvously would not work in a case where
+                    // a plan is defined with multiple phases of the same type.
+                    final PlanPhaseSpecifier curPlanPhaseSpecifier = input.getPlanPhaseSpecifier();
+                    if (curPlanPhaseSpecifier.getPhaseType().equals(curPhase.getPhaseType())) {
+                        return true;
+                    }
+                    return false;
+                }
+            }).orNull();
+            resolvedOverride[index++] = curOverride != null ?
+                                        new DefaultPlanPhasePriceOverride(curPhase.getName(), curOverride.getCurrency(), curOverride.getFixedPrice(), curOverride.getRecurringPrice()) :
+                                        null;
+        }
+
+        final CatalogOverridePlanDefinitionModelDao overriddenPlan = overrideDao.getOrCreateOverridePlanDefinition(parentPlan.getName(), catalogEffectiveDate, resolvedOverride, context);
+        final String planName = new StringBuffer(parentPlan.getName()).append("-").append(overriddenPlan.getRecordId()).toString();
+        final DefaultPlan result  = new DefaultPlan(planName, (DefaultPlan) parentPlan, resolvedOverride);
+        return result;
     }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/override/PriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/override/PriceOverride.java
index 5303b8d..b60befe 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/override/PriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/override/PriceOverride.java
@@ -19,11 +19,14 @@ package org.killbill.billing.catalog.override;
 
 import java.util.List;
 
+import org.joda.time.DateTime;
+import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.DefaultPlan;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 
 public interface PriceOverride {
-    DefaultPlan getOverriddenPlan(Plan parentPlan, List<PlanPhasePriceOverride> overrides) throws CatalogApiException;
+
+    DefaultPlan getOrCreateOverriddenPlan(final Plan parentPlan, final DateTime catalogEffectiveDate, final List<PlanPhasePriceOverride> overrides, final InternalCallContext context) throws CatalogApiException;
 }
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 399db49..a603547 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
@@ -92,8 +92,6 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
 
     private URI catalogURI;
 
-    private PriceOverride priceOverride;
-
     public StandaloneCatalog() {
     }
 
@@ -165,7 +163,7 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
       * @see org.killbill.billing.catalog.ICatalog#getPlan(java.lang.String, java.lang.String)
       */
     @Override
-    public DefaultPlan findCurrentPlan(final String productName, final BillingPeriod period, final String priceListName, final List<PlanPhasePriceOverride> overrides) throws CatalogApiException {
+    public DefaultPlan findCurrentPlan(final String productName, final BillingPeriod period, final String priceListName, final List<PlanPhasePriceOverride> nullOverrides) throws CatalogApiException {
         if (productName == null) {
             throw new CatalogApiException(ErrorCode.CAT_NULL_PRODUCT_NAME);
         }
@@ -178,11 +176,7 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
             final String periodString = (period == null) ? "NULL" : period.toString();
             throw new CatalogApiException(ErrorCode.CAT_PLAN_NOT_FOUND, productName, periodString, priceListName);
         }
-        if (overrides != null && !overrides.isEmpty()) {
-            return priceOverride.getOverriddenPlan(result, overrides);
-        } else {
-            return  result;
-        }
+        return result;
     }
 
     @Override
@@ -352,10 +346,6 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         return this;
     }
 
-    public void setPriceOverride(final PriceOverride priceOverride) {
-        this.priceOverride = priceOverride;
-    }
-
     @Override
     public boolean canCreatePlan(final PlanSpecifier specifier) throws CatalogApiException {
         final Product product = findCurrentProduct(specifier.getProductName());
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
new file mode 100644
index 0000000..3564fb1
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 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.net.URI;
+import java.util.Date;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.callcontext.InternalCallContext;
+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.BillingPeriod;
+import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.Listing;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanAlignmentChange;
+import org.killbill.billing.catalog.api.PlanAlignmentCreate;
+import org.killbill.billing.catalog.api.PlanChangeResult;
+import org.killbill.billing.catalog.api.PlanPhase;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PlanSpecifier;
+import org.killbill.billing.catalog.api.PriceList;
+import org.killbill.billing.catalog.api.Product;
+import org.killbill.billing.catalog.api.StaticCatalog;
+import org.killbill.billing.catalog.api.Unit;
+import org.killbill.billing.catalog.override.PriceOverride;
+import org.killbill.xmlloader.ValidatingConfig;
+import org.killbill.xmlloader.ValidationErrors;
+
+public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<StandaloneCatalogWithPriceOverride> implements StaticCatalog {
+
+    private final StandaloneCatalog standaloneCatalog;
+    private final PriceOverride priceOverride;
+
+    public StandaloneCatalogWithPriceOverride(final StandaloneCatalog staticCatalog, final PriceOverride priceOverride) {
+        this.standaloneCatalog = staticCatalog;
+        this.priceOverride = priceOverride;
+    }
+
+    @Override
+    public String getCatalogName() {
+        return standaloneCatalog.getCatalogName();
+    }
+
+    @Override
+    public BillingMode getRecurringBillingMode() {
+        return standaloneCatalog.getRecurringBillingMode();
+    }
+
+    @Override
+    public Date getEffectiveDate() {
+        return standaloneCatalog.getEffectiveDate();
+    }
+
+    @Override
+    public Currency[] getCurrentSupportedCurrencies() throws CatalogApiException {
+        return standaloneCatalog.getCurrentSupportedCurrencies();
+    }
+
+    @Override
+    public DefaultProduct[] getCurrentProducts() throws CatalogApiException {
+        return standaloneCatalog.getCurrentProducts();
+    }
+
+    @Override
+    public Unit[] getCurrentUnits() throws CatalogApiException {
+        return standaloneCatalog.getCurrentUnits();
+    }
+
+    @Override
+    public DefaultPlan[] getCurrentPlans() throws CatalogApiException {
+        return standaloneCatalog.getCurrentPlans();
+    }
+
+    @Override
+    public Plan findCurrentPlan(final String productName, final BillingPeriod period, final String priceListName, final List<PlanPhasePriceOverride> overrides) throws CatalogApiException {
+        final Plan defaultPlan = standaloneCatalog.findCurrentPlan(productName, period, priceListName, null);
+        if (overrides == null || overrides.isEmpty()) {
+            return defaultPlan;
+        }
+        // STEPH_PO Hum, how am i supposed to create a context here???
+        final InternalCallContext context = null;
+        return priceOverride.getOrCreateOverriddenPlan(defaultPlan, new DateTime(getEffectiveDate()), overrides, context);
+    }
+
+    @Override
+    public Plan findCurrentPlan(final String planName) throws CatalogApiException {
+        return standaloneCatalog.findCurrentPlan(planName);
+    }
+
+    @Override
+    public Product findCurrentProduct(final String productName) throws CatalogApiException {
+        return standaloneCatalog.findCurrentProduct(productName);
+    }
+
+    @Override
+    public PlanPhase findCurrentPhase(final String phaseName) throws CatalogApiException {
+        return standaloneCatalog.findCurrentPhase(phaseName);
+    }
+
+    @Override
+    public PriceList findCurrentPricelist(final String priceListName) throws CatalogApiException {
+        return standaloneCatalog.findCurrentPricelist(priceListName);
+    }
+
+    @Override
+    public BillingActionPolicy planChangePolicy(final PlanPhaseSpecifier planPhaseSpecifier, final PlanSpecifier planSpecifier) throws CatalogApiException {
+        return standaloneCatalog.planChangePolicy(planPhaseSpecifier, planSpecifier);
+    }
+
+    @Override
+    public PlanChangeResult planChange(final PlanPhaseSpecifier planPhaseSpecifier, final PlanSpecifier planSpecifier) throws CatalogApiException {
+        return standaloneCatalog.planChange(planPhaseSpecifier, planSpecifier);
+    }
+
+    @Override
+    public BillingActionPolicy planCancelPolicy(final PlanPhaseSpecifier planPhaseSpecifier) throws CatalogApiException {
+        return standaloneCatalog.planCancelPolicy(planPhaseSpecifier);
+    }
+
+    @Override
+    public PlanAlignmentCreate planCreateAlignment(final PlanSpecifier planSpecifier) throws CatalogApiException {
+        return standaloneCatalog.planCreateAlignment(planSpecifier);
+    }
+
+    @Override
+    public BillingAlignment billingAlignment(final PlanPhaseSpecifier planPhaseSpecifier) throws CatalogApiException {
+        return standaloneCatalog.billingAlignment(planPhaseSpecifier);
+    }
+
+    @Override
+    public PlanAlignmentChange planChangeAlignment(final PlanPhaseSpecifier planPhaseSpecifier, final PlanSpecifier planSpecifier) throws CatalogApiException {
+        return standaloneCatalog.planChangeAlignment(planPhaseSpecifier, planSpecifier);
+    }
+
+    @Override
+    public boolean canCreatePlan(final PlanSpecifier planSpecifier) throws CatalogApiException {
+        return standaloneCatalog.canCreatePlan(planSpecifier);
+    }
+
+    @Override
+    public List<Listing> getAvailableBasePlanListings() throws CatalogApiException {
+        return standaloneCatalog.getAvailableBasePlanListings();
+    }
+
+    @Override
+    public List<Listing> getAvailableAddOnListings(final String baseProductName, @Nullable final String priceListName) throws CatalogApiException {
+        return standaloneCatalog.getAvailableAddOnListings(baseProductName, priceListName);
+    }
+
+    @Override
+    public boolean compliesWithLimits(final String phaseName, final String unit, final double value) throws CatalogApiException {
+        return standaloneCatalog.compliesWithLimits(phaseName, unit, value);
+    }
+
+    @Override
+    public ValidationErrors validate(final StandaloneCatalogWithPriceOverride root, final ValidationErrors errors) {
+        return standaloneCatalog.validate(root.standaloneCatalog, errors);
+    }
+
+    @Override
+    public void initialize(final StandaloneCatalogWithPriceOverride root, final URI sourceURI) {
+        standaloneCatalog.initialize(root.standaloneCatalog, sourceURI);
+    }
+
+    public DefaultPriceList findCurrentPriceList(final String priceListName) throws CatalogApiException {
+        return standaloneCatalog.findCurrentPriceList(priceListName);
+    }
+
+
+}
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 2bfb1ec..e647ad5 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
@@ -60,14 +60,14 @@ import org.killbill.xmlloader.ValidationErrors;
 
 @XmlRootElement(name = "catalog")
 @XmlAccessorType(XmlAccessType.NONE)
-public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implements Catalog, StaticCatalog {
+public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPriceOverride> implements Catalog, StaticCatalog {
 
     private final Clock clock;
     private String catalogName;
     private BillingMode recurringBillingMode;
 
     @XmlElement(name = "catalogVersion", required = true)
-    private final List<StandaloneCatalog> versions = new ArrayList<StandaloneCatalog>();
+    private final List<StandaloneCatalogWithPriceOverride> versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
 
     // Default CTOR for XMLWriter.writeXML
     public VersionedCatalog() {
@@ -82,12 +82,12 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implem
     //
     // Private methods
     //
-    private StandaloneCatalog versionForDate(final DateTime date) throws CatalogApiException {
+    private StandaloneCatalogWithPriceOverride versionForDate(final DateTime date) throws CatalogApiException {
         return versions.get(indexOfVersionForDate(date.toDate()));
     }
 
-    private List<StandaloneCatalog> versionsBeforeDate(final Date date) throws CatalogApiException {
-        final List<StandaloneCatalog> result = new ArrayList<StandaloneCatalog>();
+    private List<StandaloneCatalogWithPriceOverride> versionsBeforeDate(final Date date) throws CatalogApiException {
+        final List<StandaloneCatalogWithPriceOverride> result = new ArrayList<StandaloneCatalogWithPriceOverride>();
         final int index = indexOfVersionForDate(date);
         for (int i = 0; i <= index; i++) {
             result.add(versions.get(i));
@@ -97,7 +97,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implem
 
     private int indexOfVersionForDate(final Date date) throws CatalogApiException {
         for (int i = versions.size() - 1; i >= 0; i--) {
-            final StandaloneCatalog c = versions.get(i);
+            final StandaloneCatalogWithPriceOverride c = versions.get(i);
             if (c.getEffectiveDate().getTime() <= date.getTime()) {
                 return i;
             }
@@ -125,7 +125,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implem
             this.overrides = overrides;
         }
 
-        public Plan findPlan(final StandaloneCatalog catalog) throws CatalogApiException {
+        public Plan findPlan(final StandaloneCatalogWithPriceOverride catalog) throws CatalogApiException {
             if (name != null) {
                 return catalog.findCurrentPlan(name);
             } else {
@@ -138,13 +138,13 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implem
                           final DateTime requestedDate,
                           final DateTime subscriptionStartDate)
             throws CatalogApiException {
-        final List<StandaloneCatalog> catalogs = versionsBeforeDate(requestedDate.toDate());
+        final List<StandaloneCatalogWithPriceOverride> catalogs = versionsBeforeDate(requestedDate.toDate());
         if (catalogs.size() == 0) {
             throw new CatalogApiException(ErrorCode.CAT_NO_CATALOG_FOR_GIVEN_DATE, requestedDate.toDate().toString());
         }
 
         for (int i = catalogs.size() - 1; i >= 0; i--) { // Working backwards to find the latest applicable plan
-            final StandaloneCatalog c = catalogs.get(i);
+            final StandaloneCatalogWithPriceOverride c = catalogs.get(i);
             Plan plan;
             try {
                 plan = wrapper.findPlan(c);
@@ -176,7 +176,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implem
     //
     // Public methods not exposed in interface
     //
-    public void add(final StandaloneCatalog e) throws CatalogApiException {
+    public void add(final StandaloneCatalogWithPriceOverride e) throws CatalogApiException {
         if (catalogName == null) {
             catalogName = e.getCatalogName();
         } else {
@@ -192,15 +192,15 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implem
             }
         }
         versions.add(e);
-        Collections.sort(versions, new Comparator<StandaloneCatalog>() {
+        Collections.sort(versions, new Comparator<StandaloneCatalogWithPriceOverride>() {
             @Override
-            public int compare(final StandaloneCatalog c1, final StandaloneCatalog c2) {
+            public int compare(final StandaloneCatalogWithPriceOverride c1, final StandaloneCatalogWithPriceOverride c2) {
                 return c1.getEffectiveDate().compareTo(c2.getEffectiveDate());
             }
         });
     }
 
-    public Iterator<StandaloneCatalog> iterator() {
+    public Iterator<StandaloneCatalogWithPriceOverride> iterator() {
         return versions.iterator();
     }
 
@@ -346,15 +346,15 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implem
     // VerifiableConfig API
     //
     @Override
-    public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
-        for (final StandaloneCatalog c : versions) {
+    public void initialize(final StandaloneCatalogWithPriceOverride catalog, final URI sourceURI) {
+        for (final StandaloneCatalogWithPriceOverride c : versions) {
             c.initialize(catalog, sourceURI);
         }
     }
 
     @Override
-    public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
-        for (final StandaloneCatalog c : versions) {
+    public ValidationErrors validate(final StandaloneCatalogWithPriceOverride catalog, final ValidationErrors errors) {
+        for (final StandaloneCatalogWithPriceOverride c : versions) {
             errors.addAll(c.validate(c, errors));
         }
         //TODO MDW validation - ensure all catalog versions have a single name
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java b/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java
index 64d5075..9f0d0f9 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java
@@ -32,6 +32,7 @@ import javax.xml.transform.TransformerException;
 import org.joda.time.DateTime;
 import org.killbill.billing.catalog.CatalogTestSuiteNoDB;
 import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.InvalidConfigException;
@@ -119,7 +120,7 @@ public class TestVersionedCatalogLoader extends CatalogTestSuiteNoDB {
     public void testLoad() throws IOException, SAXException, InvalidConfigException, JAXBException, TransformerException, URISyntaxException, CatalogApiException {
         final VersionedCatalog c = loader.load(Resources.getResource("versionedCatalog").toString());
         Assert.assertEquals(c.size(), 3);
-        final Iterator<StandaloneCatalog> it = c.iterator();
+        final Iterator<StandaloneCatalogWithPriceOverride> it = c.iterator();
         DateTime dt = new DateTime("2011-01-01T00:00:00+00:00");
         Assert.assertEquals(it.next().getEffectiveDate(), dt.toDate());
         dt = new DateTime("2011-02-02T00:00:00+00:00");
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/MockInternationalPrice.java b/catalog/src/test/java/org/killbill/billing/catalog/MockInternationalPrice.java
index 1500ce1..1e7f747 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/MockInternationalPrice.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/MockInternationalPrice.java
@@ -34,6 +34,7 @@ public class MockInternationalPrice extends DefaultInternationalPrice {
         return new MockInternationalPrice(new DefaultPrice().setCurrency(Currency.USD).setValue(new BigDecimal(value)));
     }
 
+
     public MockInternationalPrice(final DefaultPrice... price) {
         setPrices(price);
     }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java b/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
index b004cd8..08adf80 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
@@ -52,7 +52,7 @@ public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testAddCatalog() throws IOException, SAXException, InvalidConfigException, JAXBException, TransformerException, URISyntaxException, ServiceException, CatalogApiException {
-        vc.add((new StandaloneCatalog(new Date()).setCatalogName(vc.getCatalogName()).setRecurringBillingMode(vc.getRecurringBillingMode())));
+        vc.add(new StandaloneCatalogWithPriceOverride(new StandaloneCatalog(new Date()).setCatalogName(vc.getCatalogName()).setRecurringBillingMode(vc.getRecurringBillingMode()), null));
         Assert.assertEquals(vc.size(), 4);
     }
 
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 25e526e..25c4de8 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
@@ -123,8 +123,12 @@ public class PhasePriceOverrideJson {
             @Override
             public PlanPhasePriceOverride apply(@Nullable final PhasePriceOverrideJson input) {
                 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);
-                return new DefaultPlanPhasePriceOverride(input.getPhaseName(), planPhaseSpecifier, currency, input.getFixedPrice(), input.getRecurringPrice());
+                if (input.getPhaseName() != null) {
+                    return new DefaultPlanPhasePriceOverride(input.getPhaseName(), currency, input.getFixedPrice(), input.getRecurringPrice());
+                } else {
+                    final PlanPhaseSpecifier planPhaseSpecifier = new PlanPhaseSpecifier(spec.getProductName(), spec.getProductCategory(), 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/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index e5ef133..497756c 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
@@ -925,7 +925,11 @@ public class InvoiceResource extends JaxRsResourceBase {
                     @Nullable
                     @Override
                     public PlanPhasePriceOverride apply(@Nullable final PhasePriceOverrideJson input) {
-                        return new DefaultPlanPhasePriceOverride(input.getPhaseName(), planPhaseSpecifier, currency, input.getFixedPrice(), input.getRecurringPrice());
+                        if (input.getPhaseName() != null) {
+                            return new DefaultPlanPhasePriceOverride(input.getPhaseName(), currency, input.getFixedPrice(), input.getRecurringPrice());
+                        } else {
+                            return new DefaultPlanPhasePriceOverride(planPhaseSpecifier, currency, input.getFixedPrice(), input.getRecurringPrice());
+                        }
                     }
                 })) : ImmutableList.<PlanPhasePriceOverride>of();
             }
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 cc7ccd4..e1eb4dc 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
@@ -249,7 +249,6 @@ public class SubscriptionResource extends JaxRsResourceBase {
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
 
         final UUID accountId = entitlement.getAccountId() != null ? UUID.fromString(entitlement.getAccountId()) : null;
-        final Account account = accountUserApi.getAccountById(accountId, callContext);
         final EntitlementCallCompletionCallback<Response> callback = new EntitlementCallCompletionCallback<Response>() {
 
             private boolean isImmediateOp = true;
@@ -263,6 +262,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
                 final LocalDate inputLocalDate = toLocalDate(current.getAccountId(), requestedDate, callContext);
                 final Entitlement newEntitlement;
 
+                final Account account = accountUserApi.getAccountById(accountId, callContext);
                 final PlanSpecifier planSpec = new PlanSpecifier(entitlement.getProductName(),
                                                                  ProductCategory.valueOf(entitlement.getProductCategory()),
                                                                  BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList());
diff --git a/profiles/killbill/src/main/resources/CatalogMos.xml b/profiles/killbill/src/main/resources/CatalogMos.xml
new file mode 100644
index 0000000..f86c487
--- /dev/null
+++ b/profiles/killbill/src/main/resources/CatalogMos.xml
@@ -0,0 +1,515 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="catalog.xsd ">
+
+    <effectiveDate>2011-01-01T00:00:00+00:00</effectiveDate>
+    <catalogName>MOSCatalog</catalogName>
+
+    <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
+
+    <currencies>
+        <currency>USD</currency>
+    </currencies>
+
+    <products>
+        <product name="cafe">
+            <category>BASE</category>
+        </product>
+        <product name="custom">
+            <category>BASE</category>
+        </product>
+        <product name="flagship">
+            <category>BASE</category>
+        </product>
+        <product name="free">
+            <category>BASE</category>
+        </product>
+        <product name="mx60mr12_2_license">
+            <category>BASE</category>
+        </product>
+        <product name="mx60mr12_3_license">
+            <category>BASE</category>
+        </product>
+        <product name="mx60mr12_4_license">
+            <category>BASE</category>
+        </product>
+        <product name="mx60mr12_5_license">
+            <category>BASE</category>
+        </product>
+        <product name="mx60_license">
+            <category>BASE</category>
+        </product>
+        <product name="mx60mr12_license">
+            <category>BASE</category>
+        </product>
+        <product name="mx80mr12_6_license">
+            <category>BASE</category>
+        </product>
+        <product name="z1_mr12">
+            <category>BASE</category>
+        </product>
+        <product name="z1_license">
+            <category>BASE</category>
+        </product>
+        <product name="midsize">
+            <category>BASE</category>
+        </product>
+        <product name="mr12_license">
+            <category>BASE</category>
+        </product>
+        <product name="solo">
+            <category>BASE</category>
+        </product>
+    </products>
+
+    <rules>
+        <changePolicy>
+            <changePolicyCase>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+        </changePolicy>
+        <changeAlignment>
+            <changeAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </changeAlignmentCase>
+        </changeAlignment>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <policy>END_OF_TERM</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
+        <createAlignment>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+        </createAlignment>
+        <billingAlignment>
+            <billingAlignmentCase>
+                <alignment>SUBSCRIPTION</alignment>
+            </billingAlignmentCase>
+        </billingAlignment>
+        <priceList>
+            <priceListCase>
+                <toPriceList>DEFAULT</toPriceList>
+            </priceListCase>
+        </priceList>
+    </rules>
+
+    <plans>
+        <plan name="cafe-monthly">
+            <product>cafe</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>199.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="custom-monthly">
+            <product>custom</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>450.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="flagship-monthly">
+            <product>flagship</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>399.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="free-monthly"> <!-- May be alternative ways to model this - not sure if it matters though -->
+            <product>free</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>0.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="mx60mr12_2_license-annual">
+            <product>mx60mr12_2_license</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>YEARS</unit>
+                        <number>1</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>206.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="mx60mr12_3_license-annual">
+            <product>mx60mr12_3_license</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>YEARS</unit>
+                        <number>1</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>262.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="mx60mr12_4_license-annual">
+            <product>mx60mr12_4_license</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>YEARS</unit>
+                        <number>1</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>318.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="mx60mr12_5_license-annual">
+            <product>mx60mr12_5_license</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>YEARS</unit>
+                        <number>1</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>374.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="mx60_license-annual">
+            <product>mx60_license</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>YEARS</unit>
+                        <number>1</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>94.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="mx60mr12_license-annual">
+            <product>mx60mr12_license</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>YEARS</unit>
+                        <number>1</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>150.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="mx80mr12_6_license-annual">
+            <product>mx80mr12_6_license</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>YEARS</unit>
+                        <number>1</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>696.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="z1_mr12-annual">
+            <product>z1_mr12</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>YEARS</unit>
+                        <number>1</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>71.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="z1_license-annual">
+            <product>z1_license</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>YEARS</unit>
+                        <number>1</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>15.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="midsize-monthly">
+            <product>midsize</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>299.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="mr12_license-annual">
+            <product>mr12_license</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>YEARS</unit>
+                        <number>1</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>56.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="solo-monthly">
+            <product>solo</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>99.00</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+    </plans>
+    <priceLists>
+        <defaultPriceList name="DEFAULT">
+            <plans>
+                <plan>cafe-monthly</plan>
+                <plan>custom-monthly</plan>
+                <plan>flagship-monthly</plan>
+                <plan>free-monthly</plan>
+                <plan>mx60mr12_2_license-annual</plan>
+                <plan>mx60mr12_3_license-annual</plan>
+                <plan>mx60mr12_4_license-annual</plan>
+                <plan>mx60mr12_5_license-annual</plan>
+                <plan>mx60_license-annual</plan>
+                <plan>mx60mr12_license-annual</plan>
+                <plan>mx80mr12_6_license-annual</plan>
+                <plan>z1_mr12-annual</plan>
+                <plan>z1_license-annual</plan>
+                <plan>midsize-monthly</plan>
+                <plan>mr12_license-annual</plan>
+                <plan>solo-monthly</plan>
+            </plans>
+        </defaultPriceList>
+    </priceLists>
+</catalog>
diff --git a/profiles/killbill/src/main/resources/killbill-server.properties.mos b/profiles/killbill/src/main/resources/killbill-server.properties.mos
new file mode 100644
index 0000000..735f895
--- /dev/null
+++ b/profiles/killbill/src/main/resources/killbill-server.properties.mos
@@ -0,0 +1,88 @@
+#
+# Copyright 2010-2013 Ning, Inc.
+# Copyright 2014 Groupon, Inc
+# Copyright 2014 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.
+#
+
+#
+# KILLBILL GENERIC PROPERTIES
+#
+# Database config
+#org.killbill.dao.url=jdbc:mysql://127.0.0.1:3306/killbill
+org.killbill.dao.url=jdbc:mysql://127.0.0.1:3306/killbil_analytics_mos
+org.killbill.dao.user=root
+org.killbill.dao.password=root
+org.killbill.dao.logLevel=DEBUG
+
+# Use the SpyCarAdvanced.xml catalog
+#org.killbill.catalog.uri=SpyCarAdvanced.xml
+org.killbill.catalog.uri=CatalogMos.xml
+
+# NotificationQ, Bus, ExtBus config
+org.killbill.notificationq.main.sleep=1000
+org.killbill.notificationq.main.claimed=10
+org.killbill.notificationq.main.sticky=true
+
+org.killbill.persistent.bus.external.sticky=true
+org.killbill.persistent.bus.external.inMemory=true
+
+org.killbill.persistent.bus.main.sticky=true
+org.killbill.persistent.bus.main.claimed=1
+org.killbill.persistent.bus.main.inflight.claimed=1
+org.killbill.persistent.bus.main.nbThreads=1
+org.killbill.persistent.bus.main.sleep=0
+org.killbill.persistent.bus.main.useInflightQ=true
+org.killbill.persistent.bus.main.queue.capacity=100
+
+# Start KB in multi-tenant
+org.killbill.server.multitenant=true
+
+# Override polling from Tenant Broadcast Task
+org.killbill.tenant.broadcast.rate=1s
+
+#
+# PLUGIN SPECIFIC PROPERTIES
+#
+# Database config (OSGI plugins)
+#org.killbill.billing.osgi.dao.url=jdbc:mysql://127.0.0.1:3306/killbill
+org.killbill.billing.osgi.dao.url=jdbc:mysql://127.0.0.1:3306/killbil_analytics_mos
+org.killbill.billing.osgi.dao.user=root
+org.killbill.billing.osgi.dao.password=root
+
+# Allow jruby concurrency
+org.killbill.jruby.context.scope=THREADSAFE
+
+# Path for plugin config
+#org.killbill.billing.osgi.bundles.jruby.conf.dir=/var/tmp/bundles/plugins/config
+org.killbill.osgi.bundle.install.dir=/var/tmp/bundles_analytics_mos
+
+# Config property files for plugin to access
+org.killbill.server.properties=/Users/sbrossier/Src/killbill/killbill/profiles/killbill/src/main/resources/killbill-server.properties
+
+#
+# INTEGRATION TESTS ONLY
+#
+# To enable test endpoint and have Kill Bill run with a ClockMock (should not be used for production server)
+org.killbill.server.test.mode=true
+
+# Set payment calls to timeout after 5 sec -- mostly for integration tests
+org.killbill.payment.plugin.timeout=5s
+
+org.killbill.payment.retry.days=
+
+org.killbill.catalog.bundlePath=CatalogTranslation
+org.killbill.template.bundlePath=InvoiceTranslation
+org.killbill.template.name=HtmlInvoiceTemplate.mustache
+
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
index a885df7..39c2c7c 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
@@ -64,8 +64,10 @@ public class TestEntitlement extends TestJaxrsBase {
         final String newProductName = "Assault-Rifle";
 
         final Subscription newInput = new Subscription();
+        newInput.setAccountId(entitlementJson.getAccountId());
         newInput.setSubscriptionId(entitlementJson.getSubscriptionId());
         newInput.setProductName(newProductName);
+        newInput.setProductCategory(ProductCategory.BASE);
         newInput.setBillingPeriod(entitlementJson.getBillingPeriod());
         newInput.setPriceList(entitlementJson.getPriceList());
         objFromJson = killBillClient.updateSubscription(newInput, CALL_COMPLETION_TIMEOUT_SEC, createdBy, reason, comment);
@@ -128,8 +130,10 @@ public class TestEntitlement extends TestJaxrsBase {
     public void testWithNonExistentEntitlement() throws Exception {
         final UUID subscriptionId = UUID.randomUUID();
         final Subscription subscription = new Subscription();
+        subscription.setAccountId(UUID.randomUUID());
         subscription.setSubscriptionId(subscriptionId);
         subscription.setProductName("Pistol");
+        subscription.setProductCategory(ProductCategory.BASE);
         subscription.setBillingPeriod(BillingPeriod.ANNUAL);
         subscription.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
@@ -175,8 +179,10 @@ public class TestEntitlement extends TestJaxrsBase {
 
         // Change billing period immediately
         final Subscription newInput = new Subscription();
+        newInput.setAccountId(accountJson.getAccountId());
         newInput.setSubscriptionId(subscriptionJson.getSubscriptionId());
         newInput.setProductName(subscriptionJson.getProductName());
+        newInput.setProductCategory(ProductCategory.BASE);
         newInput.setBillingPeriod(BillingPeriod.MONTHLY);
         newInput.setPriceList(subscriptionJson.getPriceList());
         objFromJson = killBillClient.updateSubscription(newInput, BillingActionPolicy.IMMEDIATE, CALL_COMPLETION_TIMEOUT_SEC, createdBy, reason, comment);