killbill-memoizeit

catalog: Add new error code CAT_MULTIPLE_MATCHING_PLANS_FOR_PRICELIST

8/22/2016 8:54:21 PM

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 c032ac5..53cf0e3 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
@@ -24,6 +24,7 @@ import javax.inject.Inject;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountData;
 import org.killbill.billing.api.TestApiListener.NextEvent;
@@ -58,6 +59,7 @@ import com.google.common.collect.ImmutableList;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
 
 public class TestIntegrationWithCatalogUpdate extends TestIntegrationBase {
 
@@ -136,7 +138,6 @@ public class TestIntegrationWithCatalogUpdate extends TestIntegrationBase {
 
         final Entitlement baseEntitlement2 = createEntitlement("xxx-14-monthly", false);
 
-
         // Add a second plan for same product but with a 30 days trial
         final SimplePlanDescriptor desc3 = new DefaultSimplePlanDescriptor("xxx-30-monthly", "XXX", ProductCategory.BASE, account.getCurrency(), BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.DAYS, ImmutableList.<String>of());
         catalogUserApi.addSimplePlan(desc3, init, testCallContext);
@@ -157,9 +158,40 @@ public class TestIntegrationWithCatalogUpdate extends TestIntegrationBase {
         assertListenerStatus();
     }
 
+    @Test(groups = "slow")
+    public void testError_CAT_MULTIPLE_MATCHING_PLANS_FOR_PRICELIST() throws Exception {
+
+        // Create a per-tenant catalog with one plan
+        final SimplePlanDescriptor desc1 = new DefaultSimplePlanDescriptor("zoe-monthly", "Zoe", 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);
+
+        final SimplePlanDescriptor desc2 = new DefaultSimplePlanDescriptor("zoe-14-monthly", "Zoe", ProductCategory.BASE, account.getCurrency(), BigDecimal.TEN, BillingPeriod.MONTHLY, 14, TimeUnit.DAYS, ImmutableList.<String>of());
+        catalogUserApi.addSimplePlan(desc2, init, testCallContext);
+        catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
+        assertEquals(catalog.getCurrentPlans().length, 2);
+
+        try {
+            final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Zoe", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+            entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, null, null, false, ImmutableList.<PluginProperty>of(), testCallContext);
+            fail("Creating entitlement should fail");
+        } catch (final EntitlementApiException e) {
+            assertEquals(e.getCode(), ErrorCode.CAT_MULTIPLE_MATCHING_PLANS_FOR_PRICELIST.getCode());
+        }
+    }
+
     private Entitlement createEntitlement(final String planName, final boolean expectPayment) throws EntitlementApiException {
         final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(planName, null);
+        return createEntitlement(spec, expectPayment);
+    }
+
+    private Entitlement createEntitlement(final String product, final BillingPeriod billingPeriod, final String priceList, final boolean expectPayment) throws EntitlementApiException {
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(product, billingPeriod, priceList, null);
+        return createEntitlement(spec, expectPayment);
+    }
 
+    private Entitlement createEntitlement(final PlanPhaseSpecifier spec, final boolean expectPayment) throws EntitlementApiException {
         if (expectPayment) {
             busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
         } else {
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
index 0d3b457..9d4a134 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
@@ -16,7 +16,9 @@
 
 package org.killbill.billing.catalog;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -70,14 +72,15 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
       * @see org.killbill.billing.catalog.IPriceList#findPlan(org.killbill.billing.catalog.api.IProduct, org.killbill.billing.catalog.api.BillingPeriod)
       */
     @Override
-    public DefaultPlan findPlan(final Product product, final BillingPeriod period) {
+    public DefaultPlan[] findPlans(final Product product, final BillingPeriod period) {
+        final List<DefaultPlan> result = new ArrayList<DefaultPlan>(plans.length);
         for (final DefaultPlan cur : getPlans()) {
             if (cur.getProduct().equals(product) &&
                     (cur.getRecurringBillingPeriod() == null || cur.getRecurringBillingPeriod().equals(period))) {
-                return cur;
+                result.add(cur);
             }
         }
-        return null;
+        return result.toArray(new DefaultPlan[result.size()]);
     }
 
     @Override
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java
index 236cdef..7e0f5e0 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java
@@ -26,7 +26,6 @@ import java.util.List;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.CatalogApiException;
-import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.Product;
@@ -53,18 +52,25 @@ public class DefaultPriceListSet extends ValidatingConfig<StandaloneCatalog> imp
         this.childPriceLists = childPriceLists != null ? childPriceLists : new DefaultPriceList[0];
     }
 
-    public DefaultPlan getPlanFrom(final String priceListName, final Product product,
-                                   final BillingPeriod period) throws CatalogApiException {
-        DefaultPlan result = null;
+    public DefaultPlan getPlanFrom(final Product product, final BillingPeriod period, final String priceListName) throws CatalogApiException {
+
+        DefaultPlan[] plans = null;
         final DefaultPriceList pl = findPriceListFrom(priceListName);
         if (pl != null) {
-            result = pl.findPlan(product, period);
+            plans = pl.findPlans(product, period);
         }
-        if (result != null) {
-            return result;
+        if (plans.length == 0) {
+            plans = defaultPricelist.findPlans(product, period);
+        }
+        switch(plans.length) {
+            case 0:
+                return null;
+            case 1:
+                return plans[0];
+            default:
+                throw new CatalogApiException(ErrorCode.CAT_MULTIPLE_MATCHING_PLANS_FOR_PRICELIST,
+                                              priceListName, product.getName(), period);
         }
-
-        return defaultPricelist.findPlan(product, period);
     }
 
     public DefaultPriceList findPriceListFrom(final String priceListName) throws CatalogApiException {
@@ -142,8 +148,4 @@ public class DefaultPriceListSet extends ValidatingConfig<StandaloneCatalog> imp
         return result;
     }
 
-    @Override
-    public Plan getPlanListFrom(final String s, final Product product, final BillingPeriod billingPeriod) {
-        return null;
-    }
 }
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 a7511f4..a2795e3 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
@@ -181,7 +181,7 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
             }
             final String inputOrDefaultPricelist = (spec.getPriceListName() == null) ? PriceListSet.DEFAULT_PRICELIST_NAME : spec.getPriceListName();
             final Product product = findCurrentProduct(spec.getProductName());
-            result = priceLists.getPlanFrom(inputOrDefaultPricelist, product, spec.getBillingPeriod());
+            result = priceLists.getPlanFrom(product, spec.getBillingPeriod(), inputOrDefaultPricelist);
         }
         if (result == null) {
             throw new CatalogApiException(ErrorCode.CAT_PLAN_NOT_FOUND,
@@ -379,9 +379,9 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
                     for (BillingPeriod billingPeriod : BillingPeriod.values()) {
                         for (PriceList priceList : getPriceLists().getAllPriceLists()) {
                             if (priceListName == null || priceListName.equals(priceList.getName())) {
-                                Plan addonInList = priceList.findPlan(availAddon, billingPeriod);
-                                if ((addonInList != null)) {
-                                    availAddons.add(new DefaultListing(addonInList, priceList));
+                                Plan[] addonInList = priceList.findPlans(availAddon, billingPeriod);
+                                for (Plan cur : addonInList) {
+                                    availAddons.add(new DefaultListing(cur, priceList));
                                 }
                             }
                         }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestPriceListSet.java b/catalog/src/test/java/org/killbill/billing/catalog/TestPriceListSet.java
index d38438a..6f82351 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestPriceListSet.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestPriceListSet.java
@@ -52,10 +52,10 @@ public class TestPriceListSet extends CatalogTestSuiteNoDB {
         };
         final DefaultPriceListSet set = new DefaultPriceListSet(defaultPriceList, childPriceLists);
 
-        Assert.assertEquals(set.getPlanFrom(PriceListSet.DEFAULT_PRICELIST_NAME, foo, BillingPeriod.ANNUAL).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
-        Assert.assertEquals(set.getPlanFrom(PriceListSet.DEFAULT_PRICELIST_NAME, foo, BillingPeriod.MONTHLY).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
-        Assert.assertEquals(set.getPlanFrom("child", foo, BillingPeriod.ANNUAL).getFinalPhase().getPhaseType(), PhaseType.DISCOUNT);
-        Assert.assertEquals(set.getPlanFrom("child", foo, BillingPeriod.MONTHLY).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.ANNUAL, "child").getFinalPhase().getPhaseType(), PhaseType.DISCOUNT);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.MONTHLY, "child").getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
     }
 
     @Test(groups = "fast")
@@ -79,9 +79,9 @@ public class TestPriceListSet extends CatalogTestSuiteNoDB {
         };
         final DefaultPriceListSet set = new DefaultPriceListSet(defaultPriceList, childPriceLists);
 
-        Assert.assertEquals(set.getPlanFrom("child", foo, BillingPeriod.ANNUAL).getFinalPhase().getPhaseType(), PhaseType.DISCOUNT);
-        Assert.assertEquals(set.getPlanFrom("child", foo, BillingPeriod.MONTHLY).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
-        Assert.assertEquals(set.getPlanFrom(PriceListSet.DEFAULT_PRICELIST_NAME, foo, BillingPeriod.ANNUAL).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
-        Assert.assertEquals(set.getPlanFrom(PriceListSet.DEFAULT_PRICELIST_NAME, foo, BillingPeriod.MONTHLY).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.ANNUAL, "child").getFinalPhase().getPhaseType(), PhaseType.DISCOUNT);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.MONTHLY, "child").getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
     }
 }
diff --git a/util/src/test/java/org/killbill/billing/mock/MockPriceList.java b/util/src/test/java/org/killbill/billing/mock/MockPriceList.java
index 3fddaf9..0d22447 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockPriceList.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockPriceList.java
@@ -42,8 +42,10 @@ public class MockPriceList implements PriceList {
     }
 
     @Override
-    public Plan findPlan(final Product product, final BillingPeriod period) {
-        return plan;
+    public Plan[] findPlans(final Product product, final BillingPeriod period) {
+        final Plan[] result = new Plan[1];
+        result[0] = plan;
+        return result;
     }
 
     public Plan getPlan() {