Details
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 96869e4..aefc695 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * 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:
*
@@ -179,9 +181,6 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
return this.priceLists;
}
- /* (non-Javadoc)
- * @see org.killbill.billing.catalog.ICatalog#getPlan(java.lang.String, java.lang.String)
- */
@Override
public Plan createOrFindCurrentPlan(final PlanSpecifier spec, final PlanPhasePriceOverridesWithCallContext unused) throws CatalogApiException {
final Plan result;
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 f0fa2d5..b9b1115 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
@@ -160,20 +160,22 @@ public class VersionedCatalog extends ValidatingConfig<VersionedCatalog> impleme
private CatalogPlanEntry findCatalogPlanEntry(final PlanRequestWrapper wrapper,
final DateTime requestedDate,
- final DateTime subscriptionStartDate)
- throws CatalogApiException {
+ final DateTime subscriptionStartDate) throws CatalogApiException {
final List<StandaloneCatalog> catalogs = versionsBeforeDate(requestedDate.toDate());
if (catalogs.isEmpty()) {
throw new CatalogApiException(ErrorCode.CAT_NO_CATALOG_FOR_GIVEN_DATE, requestedDate.toDate().toString());
}
+ CatalogPlanEntry candidateInSubsequentCatalog = null;
for (int i = catalogs.size() - 1; i >= 0; i--) { // Working backwards to find the latest applicable plan
final StandaloneCatalog c = catalogs.get(i);
+
final Plan plan;
try {
plan = wrapper.findPlan(c);
} catch (final CatalogApiException e) {
- if (e.getCode() != ErrorCode.CAT_NO_SUCH_PLAN.getCode()) {
+ if (e.getCode() != ErrorCode.CAT_NO_SUCH_PLAN.getCode() &&
+ e.getCode() != ErrorCode.CAT_PLAN_NOT_FOUND.getCode()) {
throw e;
} else {
// If we can't find an entry it probably means the plan has been retired so we keep looking...
@@ -182,21 +184,29 @@ public class VersionedCatalog extends ValidatingConfig<VersionedCatalog> impleme
}
- final boolean initialVersion = (i == 0);
+ final boolean oldestCatalog = (i == 0);
final DateTime catalogEffectiveDate = CatalogDateHelper.toUTCDateTime(c.getEffectiveDate());
- if (initialVersion || // Prevent issue with time granularity -- see #760
- !subscriptionStartDate.isBefore(catalogEffectiveDate)) { // It's a new subscription this plan always applies
+ final boolean catalogOlderThanSubscriptionStartDate = !subscriptionStartDate.isBefore(catalogEffectiveDate);
+ if (oldestCatalog || // Prevent issue with time granularity -- see #760
+ catalogOlderThanSubscriptionStartDate) { // It's a new subscription, this plan always applies
return new CatalogPlanEntry(c, plan);
- } else { //Its an existing subscription
- if (plan.getEffectiveDateForExistingSubscriptions() != null) { //if it is null any change to this does not apply to existing subscriptions
+ } else { // It's an existing subscription
+ if (plan.getEffectiveDateForExistingSubscriptions() != null) { // If it is null, any change to this catalog does not apply to existing subscriptions
final DateTime existingSubscriptionDate = CatalogDateHelper.toUTCDateTime(plan.getEffectiveDateForExistingSubscriptions());
- if (requestedDate.isAfter(existingSubscriptionDate)) { // this plan is now applicable to existing subs
+ if (requestedDate.isAfter(existingSubscriptionDate)) { // This plan is now applicable to existing subs
return new CatalogPlanEntry(c, plan);
}
+ } else if (candidateInSubsequentCatalog == null) {
+ // Keep the most recent one
+ candidateInSubsequentCatalog = new CatalogPlanEntry(c, plan);
}
}
}
+ if (candidateInSubsequentCatalog != null) {
+ return candidateInSubsequentCatalog;
+ }
+
final PlanSpecifier spec = wrapper.getSpec();
throw new CatalogApiException(ErrorCode.CAT_PLAN_NOT_FOUND,
spec.getPlanName() != null ? spec.getPlanName() : "undefined",
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 f5513d6..b3f5bd0 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
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -27,19 +27,13 @@ import java.net.URL;
import java.util.Iterator;
import java.util.List;
-import javax.xml.bind.JAXBException;
-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;
import org.testng.Assert;
import org.testng.annotations.Test;
-import org.xml.sax.SAXException;
import com.google.common.io.Files;
import com.google.common.io.Resources;
@@ -118,14 +112,16 @@ public class TestVersionedCatalogLoader extends CatalogTestSuiteNoDB {
}
@Test(groups = "fast")
- public void testLoad() throws IOException, SAXException, InvalidConfigException, JAXBException, TransformerException, URISyntaxException, CatalogApiException {
+ public void testLoad() throws CatalogApiException {
final VersionedCatalog c = loader.loadDefaultCatalog(Resources.getResource("versionedCatalog").toString());
- Assert.assertEquals(c.size(), 3);
+ Assert.assertEquals(c.size(), 4);
final Iterator<StandaloneCatalog> 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");
Assert.assertEquals(it.next().getEffectiveDate(), dt.toDate());
+ dt = new DateTime("2011-02-03T00:00:00+00:00");
+ Assert.assertEquals(it.next().getEffectiveDate(), dt.toDate());
dt = new DateTime("2011-03-03T00:00:00+00:00");
Assert.assertEquals(it.next().getEffectiveDate(), dt.toDate());
}
@@ -145,7 +141,7 @@ public class TestVersionedCatalogLoader extends CatalogTestSuiteNoDB {
}
@Test(groups = "fast")
- public void testLoadCatalogFromInsideResourceFolder() throws CatalogApiException, URISyntaxException, IOException {
+ public void testLoadCatalogFromInsideResourceFolder() throws CatalogApiException {
final VersionedCatalog c = loader.loadDefaultCatalog("com/acme/SpyCarCustom.xml");
Assert.assertEquals(c.size(), 1);
final DateTime dt = new DateTime("2015-10-04T00:00:00+00:00");
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 b70e54f..ed7ea43 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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
@@ -22,15 +22,29 @@ import java.math.BigDecimal;
import org.joda.time.DateTime;
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.Currency;
import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanSpecifier;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
+ final DateTime dt0 = new DateTime("2010-01-01T00:00:00+00:00");
+ // WeaponsHireSmall-1.xml
+ final DateTime dt1 = new DateTime("2011-01-01T00:01:00+00:00");
+ // WeaponsHireSmall-2.xml
+ final DateTime dt2 = new DateTime("2011-02-02T00:01:00+00:00");
+ // WeaponsHireSmall-2a.xml
+ final DateTime dt2a = new DateTime("2011-02-03T00:01:00+00:00");
+ // effectiveDateForExistingSubscriptions from the catalogs 2 and 2a
+ final DateTime dt214 = new DateTime("2011-02-14T00:01:00+00:00");
+ // WeaponsHireSmall-3.xml
+ final DateTime dt3 = new DateTime("2011-03-03T00:01:00+00:00");
+
private VersionedCatalog vc;
@BeforeClass(groups = "fast")
@@ -39,16 +53,9 @@ public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
vc = loader.loadDefaultCatalog("versionedCatalog");
}
-
@Test(groups = "fast")
public void testFindPlanWithDates() throws Exception {
- final DateTime dt0 = new DateTime("2010-01-01T00:00:00+00:00");
- final DateTime dt1 = new DateTime("2011-01-01T00:01:00+00:00");
- final DateTime dt2 = new DateTime("2011-02-02T00:01:00+00:00");
- final DateTime dt214 = new DateTime("2011-02-14T00:01:00+00:00");
- final DateTime dt3 = new DateTime("2011-03-03T00:01:00+00:00");
-
- // We find it although the date provided is too early because we default to first catalog version
+ // We find it although the date provided is too early because we default to first catalog version (see also testErrorOnDateTooEarly below)
final Plan newSubPlan0 = vc.findPlan("pistol-monthly", dt0, dt0);
final Plan newSubPlan1 = vc.findPlan("pistol-monthly", dt1, dt1);
@@ -66,17 +73,66 @@ public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
final Plan exSubPlan2 = vc.findPlan("pistol-monthly", dt2, dt1);
final Plan exSubPlan214 = vc.findPlan("pistol-monthly", dt214, dt1);
final Plan exSubPlan3 = vc.findPlan("pistol-monthly", dt3, dt1);
+ // Plan added in subsequent catalog (at dt2)
+ final Plan exSubPlan4 = vc.findPlan("shotgun-quarterly", dt2, dt1);
+ final Plan exSubPlan5 = vc.findPlan("shotgun-quarterly", dt2a, dt1);
+ final Plan exSubPlan6 = vc.findPlan("shotgun-quarterly", dt214, dt1);
+ final Plan exSubPlan7 = vc.findPlan("shotgun-quarterly", dt214, dt214);
Assert.assertEquals(exSubPlan2.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("29.95"));
Assert.assertEquals(exSubPlan214.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("39.95"));
Assert.assertEquals(exSubPlan3.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("39.95"));
+ Assert.assertEquals(exSubPlan4.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("249.95"));
+ // Old price
+ Assert.assertEquals(exSubPlan5.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("249.95"));
+ // New price
+ Assert.assertEquals(exSubPlan6.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("259.95"));
+ Assert.assertEquals(exSubPlan7.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("259.95"));
+ }
+
+ // Similar to testFindPlanWithDates, but use the API with PlanSpecifier
+ @Test(groups = "fast")
+ public void testFindPlanWithDatesAndPlanSpecifier() throws Exception {
+ final PlanSpecifier pistolMonthly = new PlanSpecifier("Pistol", BillingPeriod.MONTHLY, "DEFAULT");
+ final PlanSpecifier shotgunQuarterly = new PlanSpecifier("Shotgun", BillingPeriod.QUARTERLY, "DEFAULT");
+
+ // We find it although the date provided is too early because we default to first catalog version (see also testErrorOnDateTooEarly below)
+ final Plan newSubPlan0 = vc.createOrFindPlan(pistolMonthly, null, dt0, dt0);
+
+ final Plan newSubPlan1 = vc.createOrFindPlan(pistolMonthly, null, dt1, dt1);
+ final Plan newSubPlan2 = vc.createOrFindPlan(pistolMonthly, null, dt2, dt2);
+ final Plan newSubPlan214 = vc.createOrFindPlan(pistolMonthly, null, dt214, dt214);
+ final Plan newSubPlan3 = vc.createOrFindPlan(pistolMonthly, null, dt3, dt3);
+ Assert.assertEquals(newSubPlan1.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("29.95"));
+ Assert.assertEquals(newSubPlan2.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("39.95"));
+ Assert.assertEquals(newSubPlan214.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("39.95"));
+ Assert.assertEquals(newSubPlan3.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("49.95"));
+
+ // Existing subscription
+
+ final Plan exSubPlan2 = vc.createOrFindPlan(pistolMonthly, null, dt2, dt1);
+ final Plan exSubPlan214 = vc.createOrFindPlan(pistolMonthly, null, dt214, dt1);
+ final Plan exSubPlan3 = vc.createOrFindPlan(pistolMonthly, null, dt3, dt1);
+ // Plan added in subsequent catalog (at dt2)
+ final Plan exSubPlan4 = vc.createOrFindPlan(shotgunQuarterly, null, dt2, dt1);
+ final Plan exSubPlan5 = vc.createOrFindPlan(shotgunQuarterly, null, dt2a, dt1);
+ final Plan exSubPlan6 = vc.createOrFindPlan(shotgunQuarterly, null, dt214, dt1);
+ final Plan exSubPlan7 = vc.createOrFindPlan(shotgunQuarterly, null, dt214, dt214);
+
+ Assert.assertEquals(exSubPlan2.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("29.95"));
+ Assert.assertEquals(exSubPlan214.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("39.95"));
+ Assert.assertEquals(exSubPlan3.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("39.95"));
+ Assert.assertEquals(exSubPlan4.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("249.95"));
+ // Old price
+ Assert.assertEquals(exSubPlan5.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("249.95"));
+ // New price
+ Assert.assertEquals(exSubPlan6.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("259.95"));
+ Assert.assertEquals(exSubPlan7.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("259.95"));
}
@Test(groups = "fast")
public void testErrorOnDateTooEarly() throws CatalogApiException {
- final DateTime dt0 = new DateTime("1977-01-01T00:00:00+00:00");
-
// We find it although the date provided is too early because we default to first catalog version
vc.findPlan("shotgun-monthly", dt0);
@@ -84,25 +140,26 @@ public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
// We **don't find it** because date is too early and not part of first catalog version
vc.findPlan("shotgun-quarterly", dt0);
Assert.fail("Date is too early an exception should have been thrown");
- } catch (CatalogApiException e) {
+ } catch (final CatalogApiException e) {
Assert.assertEquals(e.getCode(), ErrorCode.CAT_NO_SUCH_PLAN.getCode());
}
}
-
@Test(groups = "fast")
public void testWithDeletedPlan() throws CatalogApiException {
-
// We find it because this is version 2 whose effectiveDate is "2011-02-02T00:00:00+00:00"
- vc.findPlan("shotgun-quarterly", new DateTime("2011-02-02T00:01:00+00:00"));
+ vc.findPlan("shotgun-quarterly", dt2);
try {
// We **don't find it** because date provided matches version 3 where plan was removed
- vc.findPlan("shotgun-quarterly", new DateTime("2011-03-03T00:01:00+00:00"));
+ vc.findPlan("shotgun-quarterly", dt3);
Assert.fail("Plan has been removed");
- } catch (CatalogApiException e) {
+ } catch (final CatalogApiException e) {
Assert.assertEquals(e.getCode(), ErrorCode.CAT_NO_SUCH_PLAN.getCode());
}
+ // Similar test but for existing subscription: we want to find the plan in the original catalog in this case.
+ // This would be called for instance when computing billing events (dt3 could be a future PHASE event for instance)
+ vc.findPlan("shotgun-quarterly", dt3, dt1);
}
}
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
index f333572..5f1a214 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
~ Copyright 2010-2013 Ning, Inc.
+ ~ Copyright 2014-2018 Groupon, Inc
+ ~ Copyright 2014-2018 The Billing Project, LLC
~
- ~ Ning licenses this file to you under the Apache License, version 2.0
+ ~ 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:
~
@@ -16,7 +18,7 @@
-->
<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+ xsi:noNamespaceSchemaLocation="http://docs.killbill.io/latest/catalog.xsd">
<effectiveDate>2011-01-01T00:00:00+00:00</effectiveDate>
<catalogName>WeaponsHireSmall</catalogName>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
index 3fee3ea..0e11d5b 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
~ Copyright 2010-2013 Ning, Inc.
+ ~ Copyright 2014-2018 Groupon, Inc
+ ~ Copyright 2014-2018 The Billing Project, LLC
~
- ~ Ning licenses this file to you under the Apache License, version 2.0
+ ~ 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:
~
@@ -15,8 +17,16 @@
~ under the License.
-->
+<!---
+
+Changes compared to WeaponsHireSmall-1.xml:
+ * default change policy IMMEDIATE
+ * pistol-monthly price change
+ * new shotgun-quarterly plan
+
+-->
<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+ xsi:noNamespaceSchemaLocation="http://docs.killbill.io/latest/catalog.xsd">
<effectiveDate>2011-02-02T00:00:00+00:00</effectiveDate>
<catalogName>WeaponsHireSmall</catalogName>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2a.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2a.xml
new file mode 100644
index 0000000..c2ecfd6
--- /dev/null
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2a.xml
@@ -0,0 +1,343 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+ ~ Copyright 2010-2013 Ning, Inc.
+ ~ Copyright 2014-2018 Groupon, Inc
+ ~ Copyright 2014-2018 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.
+ -->
+
+<!---
+
+Changes compared to WeaponsHireSmall-2.xml:
+ * shotgun-quarterly price change
+
+-->
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://docs.killbill.io/latest/catalog.xsd">
+
+ <effectiveDate>2011-02-03T00:00:00+00:00</effectiveDate>
+ <catalogName>WeaponsHireSmall</catalogName>
+
+ <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
+
+ <currencies>
+ <currency>USD</currency>
+ <currency>EUR</currency>
+ <currency>GBP</currency>
+ </currencies>
+
+ <units>
+ <unit name="targets"/>
+ <unit name="misfires"/>
+ <unit name="shells"/>
+ </units>
+
+ <products>
+ <product name="Pistol">
+ <category>BASE</category>
+ </product>
+ <product name="Shotgun">
+ <category>BASE</category>
+ <limits>
+ <limit>
+ <unit>shells</unit>
+ <max>300</max>
+ </limit>
+ </limits>
+ </product>
+ <product name="Laser-Scope">
+ <category>ADD_ON</category>
+ </product>
+ <product name="Extra-Ammo">
+ <category>ADD_ON</category>
+ </product>
+ </products>
+
+ <rules>
+ <changePolicy>
+ <changePolicyCase>
+ <fromBillingPeriod>MONTHLY</fromBillingPeriod>
+ <toProduct>Shotgun</toProduct>
+ <toBillingPeriod>MONTHLY</toBillingPeriod>
+ <policy>END_OF_TERM</policy>
+ </changePolicyCase>
+ <changePolicyCase>
+ <phaseType>TRIAL</phaseType>
+ <policy>IMMEDIATE</policy>
+ </changePolicyCase>
+ <changePolicyCase>
+ <policy>IMMEDIATE</policy>
+ </changePolicyCase>
+ </changePolicy>
+ <changeAlignment>
+ <changeAlignmentCase>
+ <alignment>START_OF_SUBSCRIPTION</alignment>
+ </changeAlignmentCase>
+ </changeAlignment>
+ <cancelPolicy>
+ <cancelPolicyCase>
+ <policy>IMMEDIATE</policy>
+ </cancelPolicyCase>
+ </cancelPolicy>
+ <createAlignment>
+ <createAlignmentCase>
+ <product>Laser-Scope</product>
+ <alignment>START_OF_SUBSCRIPTION</alignment>
+ </createAlignmentCase>
+ <createAlignmentCase>
+ <alignment>START_OF_BUNDLE</alignment>
+ </createAlignmentCase>
+ </createAlignment>
+ </rules>
+
+ <plans>
+ <plan name="pistol-monthly">
+ <effectiveDateForExistingSubscriptions>2011-02-14T00:00:00+00:00</effectiveDateForExistingSubscriptions>
+
+ <product>Pistol</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <fixed>
+ <fixedPrice> <!-- empty price implies $0 -->
+ </fixedPrice>
+ </fixed>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <recurring>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>GBP</currency>
+ <value>39.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>39.95</value>
+ </price>
+ <price>
+ <currency>USD</currency>
+ <value>39.95</value>
+ </price>
+ </recurringPrice>
+ <!--
+ <limits>
+ <limit>
+ <unit>targets</unit>
+ <min>3</min>
+ </limit>
+ <limit>
+ <unit>misfires</unit>
+ <max>20</max>
+ </limit>
+ </limits>
+ -->
+ </recurring>
+ </finalPhase>
+ </plan>
+ <plan name="shotgun-monthly">
+ <product>Shotgun</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <fixed>
+ <fixedPrice> <!-- empty price implies $0 -->
+ </fixedPrice>
+ </fixed>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ <number>-1</number>
+ </duration>
+ <recurring>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>249.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>149.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>169.95</value>
+ </price>
+ </recurringPrice>
+ </recurring>
+ </finalPhase>
+ </plan>
+ <plan name="shotgun-quarterly">
+ <effectiveDateForExistingSubscriptions>2011-02-14T00:00:00+00:00</effectiveDateForExistingSubscriptions>
+
+ <product>Shotgun</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <fixed>
+ <fixedPrice> <!-- empty price implies $0 -->
+ </fixedPrice>
+ </fixed>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ <number>-1</number>
+ </duration>
+ <recurring>
+ <billingPeriod>QUARTERLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>259.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>159.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>179.95</value>
+ </price>
+ </recurringPrice>
+ </recurring>
+ </finalPhase>
+ </plan>
+ <plan name="shotgun-annual">
+ <product>Shotgun</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <fixed>
+ <fixedPrice> <!-- empty price implies $0 -->
+ </fixedPrice>
+ </fixed>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <recurring>
+ <billingPeriod>ANNUAL</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>2399.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>1499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>1699.95</value>
+ </price>
+ </recurringPrice>
+ <!--
+ <limits>
+ <limit>
+ <unit>shells</unit>
+ <max>200</max>
+ </limit>
+ </limits>
+ -->
+ </recurring>
+ </finalPhase>
+ </plan>
+ <plan name="laser-scope-monthly">
+ <product>Laser-Scope</product>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <recurring>
+
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>1999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>1499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>1999.95</value>
+ </price>
+ </recurringPrice>
+ </recurring>
+ </finalPhase>
+ </plan>
+ <plan name="extra-ammo-monthly">
+ <product>Extra-Ammo</product>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <recurring>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>1999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>1499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>1999.95</value>
+ </price>
+ </recurringPrice>
+ </recurring>
+ </finalPhase>
+ </plan>
+ </plans>
+ <priceLists>
+ <defaultPriceList name="DEFAULT">
+ <plans>
+ <plan>pistol-monthly</plan>
+ <plan>shotgun-monthly</plan>
+ <plan>shotgun-quarterly</plan>
+ <plan>shotgun-annual</plan>
+ <plan>laser-scope-monthly</plan>
+ <plan>extra-ammo-monthly</plan>
+ </plans>
+ </defaultPriceList>
+ </priceLists>
+</catalog>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
index eb21229..779a440 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
~ Copyright 2010-2013 Ning, Inc.
+ ~ Copyright 2014-2018 Groupon, Inc
+ ~ Copyright 2014-2018 The Billing Project, LLC
~
- ~ Ning licenses this file to you under the Apache License, version 2.0
+ ~ 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:
~
@@ -15,8 +17,15 @@
~ under the License.
-->
+<!---
+
+Changes compared to WeaponsHireSmall-2a.xml:
+ * pistol-monthly price change
+ * shotgun-quarterly plan retired
+
+-->
<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+ xsi:noNamespaceSchemaLocation="http://docs.killbill.io/latest/catalog.xsd">
<effectiveDate>2011-03-03T00:00:00+00:00</effectiveDate>
<catalogName>WeaponsHireSmall</catalogName>
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java b/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java
index 0793d6f..57b2475 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java
@@ -243,7 +243,7 @@ public abstract class EntitlementLoggingHelper {
}
if (spec.getPriceListName() != null) {
logLine.append(", priceList='")
- .append(spec.getBillingPeriod())
+ .append(spec.getPriceListName())
.append("'");
}
logPlanPhasePriceOverrides(logLine, overrides);
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestDefaultSubscriptionBase.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestDefaultSubscriptionBase.java
index 9dff853..fab5df1 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestDefaultSubscriptionBase.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestDefaultSubscriptionBase.java
@@ -41,7 +41,8 @@ public class TestDefaultSubscriptionBase extends SubscriptionTestSuiteNoDB {
@Test(groups = "fast")
public void testCancelSOT() throws Exception {
- final DefaultSubscriptionBase subscriptionBase = new DefaultSubscriptionBase(new SubscriptionBuilder());
+ final DateTime startDate = new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC);
+ final DefaultSubscriptionBase subscriptionBase = new DefaultSubscriptionBase(new SubscriptionBuilder().setAlignStartDate(startDate));
final UUID subscriptionId = UUID.randomUUID();
final List<SubscriptionBaseEvent> inputEvents = new LinkedList<SubscriptionBaseEvent>();
@@ -52,9 +53,9 @@ public class TestDefaultSubscriptionBase extends SubscriptionTestSuiteNoDB {
.setFromDisk(true)
.setUuid(UUID.randomUUID())
.setSubscriptionId(subscriptionId)
- .setCreatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
- .setUpdatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
- .setEffectiveDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
+ .setCreatedDate(startDate)
+ .setUpdatedDate(startDate)
+ .setEffectiveDate(startDate)
.setTotalOrdering(3)
.setActive(true)));
inputEvents.add(new ApiEventCancel(new ApiEventBuilder().setApiEventType(ApiEventType.CANCEL)
@@ -64,9 +65,9 @@ public class TestDefaultSubscriptionBase extends SubscriptionTestSuiteNoDB {
.setFromDisk(false)
.setUuid(UUID.randomUUID())
.setSubscriptionId(subscriptionId)
- .setCreatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
+ .setCreatedDate(startDate)
.setUpdatedDate(null)
- .setEffectiveDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
+ .setEffectiveDate(startDate)
.setTotalOrdering(0) // In-memory event
.setActive(true)));
subscriptionBase.rebuildTransitions(inputEvents, catalog);
@@ -74,15 +75,16 @@ public class TestDefaultSubscriptionBase extends SubscriptionTestSuiteNoDB {
Assert.assertEquals(subscriptionBase.getAllTransitions().size(), 2);
Assert.assertNull(subscriptionBase.getAllTransitions().get(0).getPreviousState());
Assert.assertEquals(subscriptionBase.getAllTransitions().get(0).getNextState(), EntitlementState.ACTIVE);
- Assert.assertEquals(subscriptionBase.getAllTransitions().get(0).getEffectiveTransitionTime(), new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC));
+ Assert.assertEquals(subscriptionBase.getAllTransitions().get(0).getEffectiveTransitionTime(), startDate);
Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getPreviousState(), EntitlementState.ACTIVE);
Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getNextState(), EntitlementState.CANCELLED);
- Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getEffectiveTransitionTime(), new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC));
+ Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getEffectiveTransitionTime(), startDate);
}
@Test(groups = "fast", description = "https://github.com/killbill/killbill/issues/897")
public void testFutureCancelBeforePhase() throws Exception {
- final DefaultSubscriptionBase subscriptionBase = new DefaultSubscriptionBase(new SubscriptionBuilder());
+ final DateTime startDate = new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC);
+ final DefaultSubscriptionBase subscriptionBase = new DefaultSubscriptionBase(new SubscriptionBuilder().setAlignStartDate(startDate));
final UUID subscriptionId = UUID.randomUUID();
final List<SubscriptionBaseEvent> inputEvents = new LinkedList<SubscriptionBaseEvent>();
@@ -93,16 +95,16 @@ public class TestDefaultSubscriptionBase extends SubscriptionTestSuiteNoDB {
.setFromDisk(true)
.setUuid(UUID.randomUUID())
.setSubscriptionId(subscriptionId)
- .setCreatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
- .setUpdatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
- .setEffectiveDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
+ .setCreatedDate(startDate)
+ .setUpdatedDate(startDate)
+ .setEffectiveDate(startDate)
.setTotalOrdering(3)
.setActive(true)));
inputEvents.add(new PhaseEventData(new PhaseEventBuilder().setPhaseName("laser-scope-monthly-evergreen")
.setUuid(UUID.randomUUID())
.setSubscriptionId(subscriptionId)
- .setCreatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
- .setUpdatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
+ .setCreatedDate(startDate)
+ .setUpdatedDate(startDate)
.setEffectiveDate(new DateTime(2012, 6, 1, 0, 0, DateTimeZone.UTC))
.setTotalOrdering(4)
.setActive(true)));
@@ -113,7 +115,7 @@ public class TestDefaultSubscriptionBase extends SubscriptionTestSuiteNoDB {
.setFromDisk(false)
.setUuid(UUID.randomUUID())
.setSubscriptionId(subscriptionId)
- .setCreatedDate(new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC))
+ .setCreatedDate(startDate)
.setUpdatedDate(null)
.setEffectiveDate(new DateTime(2012, 6, 1, 0, 0, DateTimeZone.UTC))
.setTotalOrdering(0) // In-memory event
@@ -123,7 +125,7 @@ public class TestDefaultSubscriptionBase extends SubscriptionTestSuiteNoDB {
Assert.assertEquals(subscriptionBase.getAllTransitions().size(), 2);
Assert.assertNull(subscriptionBase.getAllTransitions().get(0).getPreviousState());
Assert.assertEquals(subscriptionBase.getAllTransitions().get(0).getNextState(), EntitlementState.ACTIVE);
- Assert.assertEquals(subscriptionBase.getAllTransitions().get(0).getEffectiveTransitionTime(), new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC));
+ Assert.assertEquals(subscriptionBase.getAllTransitions().get(0).getEffectiveTransitionTime(), startDate);
Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getPreviousState(), EntitlementState.ACTIVE);
Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getNextState(), EntitlementState.CANCELLED);
Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getEffectiveTransitionTime(), new DateTime(2012, 6, 1, 0, 0, DateTimeZone.UTC));