killbill-aplcache

catalog: Work related to catalog update (SimplePlan) * Fix

8/3/2016 1:32:18 AM

Details

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
index 8e8c6e2..0bbd002 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithCatalogUpdate.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithCatalogUpdate.java
@@ -35,6 +35,7 @@ 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.ProductCategory;
 import org.killbill.billing.catalog.api.SimplePlanDescriptor;
 import org.killbill.billing.catalog.api.StaticCatalog;
 import org.killbill.billing.catalog.api.TimeUnit;
@@ -88,7 +89,7 @@ public class TestIntegrationWithCatalogUpdate extends TestIntegrationBase {
     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);
+        final SimplePlanDescriptor desc1 = new DefaultSimplePlanDescriptor("foo-monthly", "Foo", ProductCategory.BASE, account.getCurrency(), BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
         catalogUserApi.addSimplePlan(desc1, init, testCallContext);
         StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
         assertEquals(catalog.getCurrentPlans().length, 1);
@@ -98,7 +99,7 @@ public class TestIntegrationWithCatalogUpdate extends TestIntegrationBase {
         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);
+        final SimplePlanDescriptor desc2 = new DefaultSimplePlanDescriptor("superfoo-monthly", "SuperFoo", ProductCategory.BASE, account.getCurrency(), new BigDecimal("20.00"), BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
         catalogUserApi.addSimplePlan(desc2, init, testCallContext);
         catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
         assertEquals(catalog.getCurrentPlans().length, 2);
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
index 4383f53..3851952 100644
--- 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
@@ -18,9 +18,11 @@
 package org.killbill.billing.catalog.api.user;
 
 import java.math.BigDecimal;
+import java.util.List;
 
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.catalog.api.SimplePlanDescriptor;
 import org.killbill.billing.catalog.api.TimeUnit;
 
@@ -28,26 +30,32 @@ public class DefaultSimplePlanDescriptor implements SimplePlanDescriptor {
 
     private final String planId;
     private final String productName;
+    private final ProductCategory productCategory;
     private final Currency currency;
     private final BigDecimal amount;
     private final BillingPeriod billingPeriod;
-    private final int trialLength;
+    private final Integer trialLength;
     private final TimeUnit trialTimeUnit;
+    private final List<String> availableBaseProducts;
 
     public DefaultSimplePlanDescriptor(final String planId,
                                        final String productName,
+                                       final ProductCategory productCategory,
                                        final Currency currency,
                                        final BigDecimal amount,
                                        final BillingPeriod billingPeriod,
-                                       final int trialLength,
-                                       final TimeUnit trialTimeUnit) {
+                                       final Integer trialLength,
+                                       final TimeUnit trialTimeUnit,
+                                       final List<String> availableBaseProducts) {
         this.planId = planId;
         this.productName = productName;
+        this.productCategory = productCategory;
         this.currency = currency;
         this.amount = amount;
         this.billingPeriod = billingPeriod;
         this.trialLength = trialLength;
         this.trialTimeUnit = trialTimeUnit;
+        this.availableBaseProducts = availableBaseProducts;
     }
 
     @Override
@@ -61,6 +69,16 @@ public class DefaultSimplePlanDescriptor implements SimplePlanDescriptor {
     }
 
     @Override
+    public ProductCategory getProductCategory() {
+        return productCategory;
+    }
+
+    @Override
+    public List<String> getAvailableBaseProducts() {
+        return availableBaseProducts;
+    }
+
+    @Override
     public Currency getCurrency() {
         return currency;
     }
@@ -76,7 +94,7 @@ public class DefaultSimplePlanDescriptor implements SimplePlanDescriptor {
     }
 
     @Override
-    public int getTrialLength() {
+    public Integer getTrialLength() {
         return trialLength;
     }
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/CatalogUpdater.java b/catalog/src/main/java/org/killbill/billing/catalog/CatalogUpdater.java
index 53c8745..0b200d2 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/CatalogUpdater.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/CatalogUpdater.java
@@ -95,19 +95,30 @@ public class CatalogUpdater {
 
     public void addSimplePlanDescriptor(final SimplePlanDescriptor desc) throws CatalogApiException {
 
-        validateSimplePlanDescriptor(desc);
+        // We need at least a planId
+        if (desc == null ||
+            desc.getPlanId() == null) {
+            throw new CatalogApiException(ErrorCode.CAT_INVALID_SIMPLE_PLAN_DESCRIPTOR, desc);
+        }
 
-        DefaultProduct product = getExistingProduct(desc.getProductName());
+        DefaultPlan plan = getExistingPlan(desc.getPlanId());
+        if (plan == null && desc.getProductName() == null) {
+            throw new CatalogApiException(ErrorCode.CAT_INVALID_SIMPLE_PLAN_DESCRIPTOR, desc);
+        }
+
+        DefaultProduct product = plan != null ? (DefaultProduct) plan.getProduct() : getExistingProduct(desc.getProductName());
         if (product == null) {
             product = new DefaultProduct();
             product.setName(desc.getProductName());
-            product.setCatagory(ProductCategory.BASE); // TODO
+            product.setCatagory(desc.getProductCategory());
             product.initialize(catalog, DUMMY_URI);
             catalog.addProduct(product);
         }
 
-        DefaultPlan plan = getExistingPlan(desc.getPlanId());
         if (plan == null) {
+
+            validateNewPlanDescriptor(desc);
+
             plan = new DefaultPlan();
             plan.setName(desc.getPlanId());
             plan.setPriceListName(DEFAULT_PRICELIST.getName());
@@ -159,6 +170,13 @@ public class CatalogUpdater {
         } catch (CatalogApiException ignore) {
             catalog.addRecurringPriceToPlan(recurring.getRecurringPrice(), new DefaultPrice().setCurrency(desc.getCurrency()).setValue(desc.getAmount()));
         }
+
+        if (desc.getProductCategory() == ProductCategory.ADD_ON) {
+            for (final String bp : desc.getAvailableBaseProducts()) {
+                catalog.addProductAvailableAO(getExistingProduct(bp), product);
+            }
+        }
+
         // Reinit catalog
         catalog.initialize(catalog, DUMMY_URI);
     }
@@ -175,8 +193,8 @@ public class CatalogUpdater {
             (plan.getInitialPhases().length == 1 &&
              (plan.getInitialPhases()[0].getPhaseType() != PhaseType.TRIAL || !plan.getInitialPhases()[0].getFixed().getPrice().isZero()))) {
             failedValidation = true;
-        } else {
 
+        } else if (desc.getTrialLength() != null && desc.getTrialTimeUnit() != null) { // If desc includes trial info we verify this is valid
             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
@@ -202,9 +220,9 @@ public class CatalogUpdater {
             } else {
 
                 // Should be same recurring BillingPeriod
-                if (plan.getFinalPhase().getRecurring().getBillingPeriod() != desc.getBillingPeriod()) {
+                if (desc.getBillingPeriod() != null && plan.getFinalPhase().getRecurring().getBillingPeriod() != desc.getBillingPeriod()) {
                     failedValidation = true;
-                } else {
+                } else if (desc.getCurrency() != null && desc.getAmount() != null) {
                     try {
                         final BigDecimal currentAmount = plan.getFinalPhase().getRecurring().getRecurringPrice().getPrice(desc.getCurrency());
                         if (currentAmount.compareTo(desc.getAmount()) != 0) {
@@ -232,15 +250,24 @@ public class CatalogUpdater {
         });
     }
 
-    private void validateSimplePlanDescriptor(final SimplePlanDescriptor desc) throws CatalogApiException {
-        if (desc == null ||
-            desc.getPlanId() == null ||
+    private void validateNewPlanDescriptor(final SimplePlanDescriptor desc) throws CatalogApiException {
+        if (desc.getProductCategory() == 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);
         }
+
+        if (desc.getProductCategory() == ProductCategory.ADD_ON) {
+            if (desc.getAvailableBaseProducts() == null || desc.getAvailableBaseProducts().isEmpty()) {
+                throw new CatalogApiException(ErrorCode.CAT_INVALID_SIMPLE_PLAN_DESCRIPTOR, desc);
+            }
+            for (final String cur : desc.getAvailableBaseProducts()) {
+                if (getExistingProduct(cur) == null) {
+                    throw new CatalogApiException(ErrorCode.CAT_INVALID_SIMPLE_PLAN_DESCRIPTOR, desc);
+                }
+            }
+        }
     }
 
     private DefaultProduct getExistingProduct(final String productName) {
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultMutableStaticCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultMutableStaticCatalog.java
index 14c10b4..4d9d167 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultMutableStaticCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultMutableStaticCatalog.java
@@ -117,6 +117,11 @@ public class DefaultMutableStaticCatalog extends StandaloneCatalog implements Mu
         currentPrices.setPrices((DefaultPrice []) newEntries);
     }
 
+    public void addProductAvailableAO(final DefaultProduct targetBasePlan, final DefaultProduct aoProduct) throws CatalogApiException {
+        final Product[] newEntries = allocateNewEntries(targetBasePlan.getAvailable(), aoProduct);
+        targetBasePlan.setAvailable((DefaultProduct[]) newEntries);
+    }
+
     private <T> T [] allocateNewEntries(final T [] existingEntries, final T newEntry) throws CatalogApiException  {
 
         // Verify entry does not already exists
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogUpdater.java b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogUpdater.java
index eab9fa3..3bc6a66 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogUpdater.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogUpdater.java
@@ -41,6 +41,7 @@ import org.killbill.xmlloader.XMLLoader;
 import org.killbill.xmlloader.XMLWriter;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.io.Resources;
 
 import static org.testng.Assert.assertEquals;
@@ -53,7 +54,7 @@ public class TestCatalogUpdater extends CatalogTestSuiteNoDB {
     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 SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("foo-monthly", "Foo", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
 
         final CatalogUpdater catalogUpdater = new CatalogUpdater("dummy", BillingMode.IN_ADVANCE, now, desc.getCurrency());
 
@@ -94,7 +95,7 @@ public class TestCatalogUpdater extends CatalogTestSuiteNoDB {
     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 SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("foo-monthly", "Foo", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 14, TimeUnit.DAYS, ImmutableList.<String>of());
 
         final CatalogUpdater catalogUpdater = new CatalogUpdater("dummy", BillingMode.IN_ADVANCE, now, desc.getCurrency());
 
@@ -148,7 +149,7 @@ public class TestCatalogUpdater extends CatalogTestSuiteNoDB {
 
         final CatalogUpdater catalogUpdater = new CatalogUpdater(originalCatalog);
 
-        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("standard-annual", "Standard", Currency.USD, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED);
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("standard-annual", "Standard", ProductCategory.BASE, Currency.USD, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
         catalogUpdater.addSimplePlanDescriptor(desc);
 
         final StandaloneCatalog catalog = catalogUpdater.getCatalog();
@@ -183,7 +184,7 @@ public class TestCatalogUpdater extends CatalogTestSuiteNoDB {
 
         final CatalogUpdater catalogUpdater = new CatalogUpdater(originalCatalog);
 
-        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.DAYS);
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.DAYS, ImmutableList.<String>of());
         catalogUpdater.addSimplePlanDescriptor(desc);
 
         final StandaloneCatalog catalog = catalogUpdater.getCatalog();
@@ -215,32 +216,32 @@ public class TestCatalogUpdater extends CatalogTestSuiteNoDB {
         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);
+        SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.DAYS, ImmutableList.<String>of());
         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);
+        desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 14, TimeUnit.DAYS, ImmutableList.<String>of());
         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);
+        desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.MONTHS, ImmutableList.<String>of());
         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);
+        desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.ANNUAL, 30, TimeUnit.DAYS, ImmutableList.<String>of());
         addBadSimplePlanDescriptor(catalogUpdater, desc);
 
         // Existing Plan has a discount phase
-        desc = new DefaultSimplePlanDescriptor("dynamic-monthly", "Dynamic", Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.MONTHS);
+        desc = new DefaultSimplePlanDescriptor("dynamic-monthly", "Dynamic", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.MONTHS, ImmutableList.<String>of());
         addBadSimplePlanDescriptor(catalogUpdater, desc);
 
         // Existing Plan has final fixedterm phase
-        desc = new DefaultSimplePlanDescriptor("superdynamic-fixedterm", "SuperDynamic", Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.DAYS);
+        desc = new DefaultSimplePlanDescriptor("superdynamic-fixedterm", "SuperDynamic", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.DAYS, ImmutableList.<String>of());
         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);
+        desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", ProductCategory.BASE, Currency.USD, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.DAYS, ImmutableList.<String>of());
         addBadSimplePlanDescriptor(catalogUpdater, desc);
     }
 
@@ -255,7 +256,7 @@ public class TestCatalogUpdater extends CatalogTestSuiteNoDB {
 
         final CatalogUpdater catalogUpdater = new CatalogUpdater(originalCatalog);
 
-        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("dynamic-annual", "Dynamic", Currency.USD, BigDecimal.TEN, BillingPeriod.MONTHLY, 14, TimeUnit.DAYS);
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("dynamic-annual", "Dynamic", ProductCategory.BASE, Currency.USD, BigDecimal.TEN, BillingPeriod.MONTHLY, 14, TimeUnit.DAYS, ImmutableList.<String>of());
         catalogUpdater.addSimplePlanDescriptor(desc);
 
         final String expectedXML = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
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
index 12913cd..1c9bb24 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SimplePlanJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SimplePlanJson.java
@@ -18,9 +18,11 @@
 package org.killbill.billing.jaxrs.json;
 
 import java.math.BigDecimal;
+import java.util.List;
 
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.catalog.api.TimeUnit;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
@@ -30,27 +32,33 @@ public class SimplePlanJson {
 
     private final String planId;
     private final String productName;
+    private final ProductCategory productCategory;
     private final Currency currency;
     private final BigDecimal amount;
     private final BillingPeriod billingPeriod;
     private final Integer trialLength;
     private final TimeUnit trialTimeUnit;
+    private final List<String> availableBaseProducts;
 
     @JsonCreator
     public SimplePlanJson(@JsonProperty("planId")  final String planId,
                           @JsonProperty("productName")  final String productName,
+                          @JsonProperty("productCategory")  final ProductCategory productCategory,
                           @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) {
+                          @JsonProperty("trialTimeUnit") final TimeUnit trialTimeUnit,
+                          @JsonProperty("availableBaseProducts") final List<String> availableBaseProducts) {
         this.planId = planId;
         this.productName = productName;
+        this.productCategory = productCategory;
         this.currency = currency;
         this.amount = amount;
         this.billingPeriod = billingPeriod;
         this.trialLength = trialLength;
         this.trialTimeUnit = trialTimeUnit;
+        this.availableBaseProducts = availableBaseProducts;
     }
 
     public String getPlanId() {
@@ -61,6 +69,10 @@ public class SimplePlanJson {
         return productName;
     }
 
+    public ProductCategory getProductCategory() {
+        return productCategory;
+    }
+
     public Currency getCurrency() {
         return currency;
     }
@@ -81,6 +93,10 @@ public class SimplePlanJson {
         return trialTimeUnit;
     }
 
+    public List<String> getAvailableBaseProducts() {
+        return availableBaseProducts;
+    }
+
     @Override
     public boolean equals(final Object o) {
         if (this == o) {
@@ -98,6 +114,9 @@ public class SimplePlanJson {
         if (productName != null ? !productName.equals(that.productName) : that.productName != null) {
             return false;
         }
+        if (productCategory != null ? !productCategory.equals(that.productCategory) : that.productCategory != null) {
+            return false;
+        }
         if (currency != that.currency) {
             return false;
         }
@@ -110,19 +129,23 @@ public class SimplePlanJson {
         if (trialLength != null ? !trialLength.equals(that.trialLength) : that.trialLength != null) {
             return false;
         }
+        if (availableBaseProducts != null ? !availableBaseProducts.equals(that.availableBaseProducts) : that.availableBaseProducts != 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 + (productCategory != null ? productCategory.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);
+        result = 31 * result + (availableBaseProducts != null ? availableBaseProducts.hashCode() : 0);
         return result;
     }
 }
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 f8ab371..9fc2098 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
@@ -35,6 +35,7 @@ import javax.ws.rs.core.UriInfo;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
@@ -121,15 +122,24 @@ public class CatalogResource extends JaxRsResourceBase {
     @ApiResponses(value = {})
     public Response getCatalogJson(@QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
                                    @javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
-        DateTime catalogDateVersion = clock.getUTCNow();
-        if (requestedDate != null) {
-            catalogDateVersion = DATE_TIME_FORMATTER.parseDateTime(requestedDate).toDateTime(DateTimeZone.UTC);
-        }
 
         final TenantContext tenantContext = context.createContext(request);
-        final Catalog catalog = catalogUserApi.getCatalog(catalogName, tenantContext);
-        final CatalogJson json = new CatalogJson(catalog, catalogDateVersion);
-        return Response.status(Status.OK).entity(json).build();
+        final DateTime catalogDateVersion = requestedDate != null ?
+                                      DATE_TIME_FORMATTER.parseDateTime(requestedDate).toDateTime(DateTimeZone.UTC) :
+                                      null;
+
+        // Yack...
+        final VersionedCatalog catalog = (VersionedCatalog) catalogUserApi.getCatalog(catalogName, tenantContext);
+
+        final List<CatalogJson> result = new ArrayList<CatalogJson>();
+        if (catalogDateVersion != null) {
+            result.add(new CatalogJson(catalog, catalogDateVersion));
+        } else {
+            for (final StandaloneCatalogWithPriceOverride v : catalog.getVersions()) {
+                result.add(new CatalogJson(catalog, new DateTime(v.getEffectiveDate())));
+            }
+        }
+        return Response.status(Status.OK).entity(result).build();
     }
 
     // Need to figure out dependency on StandaloneCatalog
@@ -200,11 +210,13 @@ public class CatalogResource extends JaxRsResourceBase {
 
         final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor(simplePlan.getPlanId(),
                                                                           simplePlan.getProductName(),
+                                                                          simplePlan.getProductCategory(),
                                                                           simplePlan.getCurrency(),
                                                                           simplePlan.getAmount(),
                                                                           simplePlan.getBillingPeriod(),
                                                                           simplePlan.getTrialLength(),
-                                                                          simplePlan.getTrialTimeUnit());
+                                                                          simplePlan.getTrialTimeUnit(),
+                                                                          simplePlan.getAvailableBaseProducts());
         catalogUserApi.addSimplePlan(desc, clock.getUTCNow(), callContext);
         return uriBuilder.buildResponse(uriInfo, CatalogResource.class, null, null);
     }