killbill-memoizeit

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 d3d525a..b791185 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultFixed.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultFixed.java
@@ -59,7 +59,7 @@ public class DefaultFixed extends ValidatingConfig<StandaloneCatalog> implements
 
     public DefaultFixed(final DefaultFixed in, final PlanPhasePriceOverride override) {
         this.type = in.getType();
-        this.fixedPrice = new DefaultInternationalPrice(fixedPrice, override, true);
+        this.fixedPrice = in.getPrice() != null ? new DefaultInternationalPrice((DefaultInternationalPrice) in.getPrice(), override, true) : null;
     }
 
     @Override
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 0cea252..ec6e80f 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultInternationalPrice.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultInternationalPrice.java
@@ -52,7 +52,7 @@ public class DefaultInternationalPrice extends ValidatingConfig<StandaloneCatalo
     public DefaultInternationalPrice() {}
 
     public DefaultInternationalPrice(final DefaultInternationalPrice in, final PlanPhasePriceOverride override, final boolean fixed) {
-        this.prices = new DefaultPrice[in.getPrices().length];
+        this.prices = in.getPrices() != null ? new DefaultPrice[in.getPrices().length] : null;
         // 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];
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 7770d74..567ec9f 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
@@ -87,8 +87,8 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
         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]);
+        for (int i = 0; i< overrides.length - 1; i++) {
+            final DefaultPlanPhase newPhase = new DefaultPlanPhase(in.getInitialPhases()[i], overrides[i]);
             initialPhases[i] = newPhase;
         }
         this.finalPhase = new DefaultPlanPhase(in.getFinalPhase(), overrides[overrides.length - 1]);
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 b28f371..02b7840 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultRecurring.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultRecurring.java
@@ -49,7 +49,7 @@ public class DefaultRecurring extends ValidatingConfig<StandaloneCatalog> implem
 
     public DefaultRecurring(final DefaultRecurring in, final PlanPhasePriceOverride override) {
         this.billingPeriod = in.getBillingPeriod();
-        this.recurringPrice = new DefaultInternationalPrice(recurringPrice, override, false);
+        this.recurringPrice = in.getRecurringPrice() != null ? new DefaultInternationalPrice(in.getRecurringPrice(), override, false) : null;
     }
 
     @Override
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 b1b20b4..88b6438 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
@@ -22,11 +22,14 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.joda.time.DateTime;
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.DefaultPlan;
+import org.killbill.billing.catalog.DefaultPlanPhase;
 import org.killbill.billing.catalog.DefaultPlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.CatalogUserApi;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
@@ -40,10 +43,11 @@ import org.killbill.billing.catalog.dao.CatalogOverridePlanDefinitionModelDao;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
+import sun.org.mozilla.javascript.internal.ast.ErrorCollector;
 
 public class DefaultPriceOverride implements PriceOverride {
 
-    final Pattern CUSTOM_PLAN_NAME_PATTERN = Pattern.compile("(.*)-(\\d+)$");
+    public static final Pattern CUSTOM_PLAN_NAME_PATTERN = Pattern.compile("(.*)-(\\d+)$");
 
     private final CatalogOverrideDao overrideDao;
 
@@ -61,8 +65,8 @@ public class DefaultPriceOverride implements PriceOverride {
             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 (input.getPhaseName() != null) {
+                        return input.getPhaseName().equals(curPhase.getName());
                     }
                     // 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.
@@ -78,6 +82,23 @@ public class DefaultPriceOverride implements PriceOverride {
                                         null;
         }
 
+        for (int i = 0; i < resolvedOverride.length; i++) {
+            final PlanPhasePriceOverride curOverride = resolvedOverride[i];
+            if (curOverride != null) {
+                final DefaultPlanPhase curPhase = (DefaultPlanPhase) parentPlan.getAllPhases()[i];
+
+                if (curPhase.getFixed() == null && curOverride.getFixedPrice() != null) {
+                    final String error = String.format("There is no existing fixed price for the phase %s", curPhase.getName());
+                    throw new CatalogApiException(ErrorCode.CAT_INVALID_INVALID_PRICE_OVERRIDE, parentPlan.getName(), error);
+                }
+
+                if (curPhase.getRecurring() == null && curOverride.getRecurringPrice() != null) {
+                    final String error = String.format("There is no existing recurring price for the phase %s", curPhase.getName());
+                    throw new CatalogApiException(ErrorCode.CAT_INVALID_INVALID_PRICE_OVERRIDE, parentPlan.getName(), error);
+                }
+            }
+        }
+
         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);
@@ -88,6 +109,9 @@ public class DefaultPriceOverride implements PriceOverride {
     public DefaultPlan getOverriddenPlan(final String planName, final StaticCatalog catalog, final InternalTenantContext context) throws CatalogApiException {
 
         final Matcher m = CUSTOM_PLAN_NAME_PATTERN.matcher(planName);
+        if (!m.matches()) {
+            throw new CatalogApiException(ErrorCode.CAT_NO_SUCH_PLAN, planName);
+        }
         final String parentPlanName = m.group(1);
         final Long planDefRecordId = Long.parseLong(m.group(2));
 
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteWithEmbeddedDB.java b/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteWithEmbeddedDB.java
index 0cbebac..a556b15 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteWithEmbeddedDB.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteWithEmbeddedDB.java
@@ -20,6 +20,7 @@ package org.killbill.billing.catalog;
 import org.killbill.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
 import org.killbill.billing.catalog.dao.CatalogOverrideDao;
 import org.killbill.billing.catalog.glue.TestCatalogModuleWithEmbeddedDB;
+import org.killbill.billing.catalog.override.PriceOverride;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.skife.jdbi.v2.IDBI;
 import org.testng.annotations.BeforeClass;
@@ -36,6 +37,9 @@ public class CatalogTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWithEm
     @Inject
     protected IDBI dbi;
 
+    @Inject
+    protected PriceOverride priceOverride;
+
     @Override
     protected KillbillConfigSource getConfigSource() {
         return getConfigSource("/resource.properties");
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverrideDao.java b/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverrideDao.java
index ee92a26..29188c3 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverrideDao.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/dao/TestCatalogOverrideDao.java
@@ -28,7 +28,6 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.xmlloader.XMLLoader;
-import org.skife.jdbi.v2.tweak.HandleCallback;
 import org.testng.annotations.Test;
 
 import com.google.common.io.Resources;
@@ -70,7 +69,6 @@ public class TestCatalogOverrideDao extends CatalogTestSuiteWithEmbeddedDB {
         assertEquals(phases.size(), 2);
     }
 
-
     @Test(groups = "slow")
     public void testGetOverriddenPlanPhases() throws Exception {
 
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestDefaultPriceOverride.java b/catalog/src/test/java/org/killbill/billing/catalog/TestDefaultPriceOverride.java
new file mode 100644
index 0000000..6f8e96b
--- /dev/null
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestDefaultPriceOverride.java
@@ -0,0 +1,182 @@
+/*
+ * 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.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+
+import org.joda.time.DateTime;
+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.Plan;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.Price;
+import org.killbill.billing.catalog.override.DefaultPriceOverride;
+import org.killbill.xmlloader.XMLLoader;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Resources;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public class TestDefaultPriceOverride extends CatalogTestSuiteWithEmbeddedDB {
+
+    @Test(groups = "slow")
+    public void testBasic() throws Exception {
+
+        final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarAdvanced.xml").toExternalForm(), StandaloneCatalog.class);
+        final Plan plan = catalog.findCurrentPlan("discount-standard-monthly");
+
+        final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
+        final PlanPhasePriceOverride phase1 = new DefaultPlanPhasePriceOverride(plan.getAllPhases()[0].getName(), Currency.USD, BigDecimal.ONE, null);
+        overrides.add(phase1);
+        final PlanPhasePriceOverride phase3 = new DefaultPlanPhasePriceOverride(plan.getAllPhases()[2].getName(), Currency.USD, null, new BigDecimal("142.41"));
+        overrides.add(phase3);
+
+        final DefaultPlan overriddenPlan = priceOverride.getOrCreateOverriddenPlan(plan, new DateTime(catalog.getEffectiveDate()), overrides, internalCallContext);
+
+        final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(overriddenPlan.getName());
+        assertTrue(m.matches());
+        assertEquals(m.group(1), plan.getName());
+        assertEquals(m.group(2), "1"); // first entry in the table
+
+        assertEquals(overriddenPlan.getProduct().getName(), plan.getProduct().getName());
+        assertEquals(overriddenPlan.getRecurringBillingPeriod(), plan.getRecurringBillingPeriod());
+        if (plan.getEffectiveDateForExistingSubscriptons() != null) {
+            assertEquals(overriddenPlan.getEffectiveDateForExistingSubscriptons().compareTo(plan.getEffectiveDateForExistingSubscriptons()), 0);
+        }
+        assertEquals(overriddenPlan.getFinalPhase().getName(), plan.getFinalPhase().getName());
+        assertEquals(overriddenPlan.getPlansAllowedInBundle(), plan.getPlansAllowedInBundle());
+
+        assertEquals(overriddenPlan.getAllPhases().length, overriddenPlan.getAllPhases().length);
+        for (int i = 0; i < overriddenPlan.getAllPhases().length; i++) {
+
+            final DefaultPlanPhase initialPhase = (DefaultPlanPhase) plan.getAllPhases()[i];
+            final DefaultPlanPhase newPhase = (DefaultPlanPhase) overriddenPlan.getAllPhases()[i];
+
+            final PlanPhasePriceOverride override = Iterables.tryFind(overrides, new Predicate<PlanPhasePriceOverride>() {
+                @Override
+                public boolean apply(final PlanPhasePriceOverride input) {
+                    return input.getPhaseName().equals(initialPhase.getName());
+                }
+            }).orNull();
+
+            assertEquals(newPhase.getName(), initialPhase.getName());
+            assertEquals(newPhase.getDuration(), initialPhase.getDuration());
+            assertEquals(newPhase.getPhaseType(), initialPhase.getPhaseType());
+            assertEquals(newPhase.getUsages().length, initialPhase.getUsages().length);
+            if (initialPhase.getFixed() != null) {
+                assertEquals(newPhase.getFixed().getType(), initialPhase.getFixed().getType());
+                assertInternationalPrice(newPhase.getFixed().getPrice(), initialPhase.getFixed().getPrice(), override, true);
+            }
+            if (initialPhase.getRecurring() != null) {
+                assertInternationalPrice(newPhase.getRecurring().getRecurringPrice(), initialPhase.getRecurring().getRecurringPrice(), override, false);
+            }
+        }
+    }
+
+    @Test(groups = "slow", expectedExceptions = CatalogApiException.class)
+    public void testWithInvalidPriceOverride() throws Exception {
+
+        final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarAdvanced.xml").toExternalForm(), StandaloneCatalog.class);
+        final Plan plan = catalog.findCurrentPlan("discount-standard-monthly");
+
+        final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
+        final PlanPhasePriceOverride phase1 = new DefaultPlanPhasePriceOverride(plan.getAllPhases()[0].getName(), Currency.USD, null, BigDecimal.ONE);
+        overrides.add(phase1);
+
+        priceOverride.getOrCreateOverriddenPlan(plan, new DateTime(catalog.getEffectiveDate()), overrides, internalCallContext);
+
+    }
+
+    @Test(groups = "slow")
+    public void testGetOverriddenPlan() throws Exception {
+
+        final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarAdvanced.xml").toExternalForm(), StandaloneCatalog.class);
+        final Plan plan = catalog.findCurrentPlan("discount-standard-monthly");
+
+        final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
+        final PlanPhasePriceOverride phase1 = new DefaultPlanPhasePriceOverride(plan.getAllPhases()[0].getName(), Currency.USD, BigDecimal.ONE, null);
+        overrides.add(phase1);
+        final PlanPhasePriceOverride phase3 = new DefaultPlanPhasePriceOverride(plan.getAllPhases()[2].getName(), Currency.USD, null, new BigDecimal("142.41"));
+        overrides.add(phase3);
+
+        final DefaultPlan overriddenPlanCreated = priceOverride.getOrCreateOverriddenPlan(plan, new DateTime(catalog.getEffectiveDate()), overrides, internalCallContext);
+
+        System.out.println("overriddenPlanCreated = " + overriddenPlanCreated.getName());
+
+        final DefaultPlan overriddenPlan = priceOverride.getOverriddenPlan(overriddenPlanCreated.getName(), catalog, internalCallContext);
+
+        assertEquals(overriddenPlan.getProduct().getName(), plan.getProduct().getName());
+        assertEquals(overriddenPlan.getRecurringBillingPeriod(), plan.getRecurringBillingPeriod());
+        if (plan.getEffectiveDateForExistingSubscriptons() != null) {
+            assertEquals(overriddenPlan.getEffectiveDateForExistingSubscriptons().compareTo(plan.getEffectiveDateForExistingSubscriptons()), 0);
+        }
+        assertEquals(overriddenPlan.getFinalPhase().getName(), plan.getFinalPhase().getName());
+        assertEquals(overriddenPlan.getPlansAllowedInBundle(), plan.getPlansAllowedInBundle());
+
+        assertEquals(overriddenPlan.getAllPhases().length, overriddenPlan.getAllPhases().length);
+        for (int i = 0; i < overriddenPlan.getAllPhases().length; i++) {
+
+            final DefaultPlanPhase initialPhase = (DefaultPlanPhase) plan.getAllPhases()[i];
+            final DefaultPlanPhase newPhase = (DefaultPlanPhase) overriddenPlan.getAllPhases()[i];
+
+            final PlanPhasePriceOverride override = Iterables.tryFind(overrides, new Predicate<PlanPhasePriceOverride>() {
+                @Override
+                public boolean apply(final PlanPhasePriceOverride input) {
+                    return input.getPhaseName().equals(initialPhase.getName());
+                }
+            }).orNull();
+
+            assertEquals(newPhase.getName(), initialPhase.getName());
+            assertEquals(newPhase.getDuration(), initialPhase.getDuration());
+            assertEquals(newPhase.getPhaseType(), initialPhase.getPhaseType());
+            assertEquals(newPhase.getUsages().length, initialPhase.getUsages().length);
+            if (initialPhase.getFixed() != null) {
+                assertEquals(newPhase.getFixed().getType(), initialPhase.getFixed().getType());
+                assertInternationalPrice(newPhase.getFixed().getPrice(), initialPhase.getFixed().getPrice(), override, true);
+            }
+            if (initialPhase.getRecurring() != null) {
+                assertInternationalPrice(newPhase.getRecurring().getRecurringPrice(), initialPhase.getRecurring().getRecurringPrice(), override, false);
+            }
+        }
+    }
+
+    private void assertInternationalPrice(final InternationalPrice newInternationalPrice, final InternationalPrice initInternationalPrice, final PlanPhasePriceOverride override, final boolean isFixed) throws CurrencyValueNull {
+        assertEquals(newInternationalPrice.getPrices().length, initInternationalPrice.getPrices().length);
+        for (int i = 0; i < newInternationalPrice.getPrices().length; i++) {
+            final Price initPrice = initInternationalPrice.getPrices()[i];
+            final Price newPrice = newInternationalPrice.getPrices()[i];
+            if (override != null && override.getCurrency() == initPrice.getCurrency() &&
+                ((isFixed && override.getFixedPrice() != null) || (!isFixed && override.getRecurringPrice() != null))) {
+                assertEquals(newPrice.getValue().compareTo(isFixed ? override.getFixedPrice() : override.getRecurringPrice()), 0);
+            } else {
+                if (initPrice != null && initPrice.getValue() != null) {
+                    assertEquals(newPrice.getValue().compareTo(initPrice.getValue()), 0);
+                }
+            }
+        }
+    }
+}