killbill-uncached

Merge remote-tracking branch 'origin/work-for-release-0.19.x'

6/17/2018 1:01:08 PM

Changes

beatrix/src/test/resources/beatrix.properties 4(+0 -4)

pom.xml 2(+1 -1)

Details

diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/BeatrixTestSuiteWithEmbeddedDB.java b/beatrix/src/test/java/org/killbill/billing/beatrix/BeatrixTestSuiteWithEmbeddedDB.java
index 29350d9..82bf4fe 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/BeatrixTestSuiteWithEmbeddedDB.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/BeatrixTestSuiteWithEmbeddedDB.java
@@ -21,10 +21,24 @@ package org.killbill.billing.beatrix;
 import org.killbill.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 
+import com.google.common.collect.ImmutableMap;
+
 public abstract class BeatrixTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWithEmbeddedDB {
 
+    final static protected ImmutableMap DEFAULT_BEATRIX_PROPERTIES = ImmutableMap.builder()
+                                                                                 .put("org.killbill.catalog.uri", "catalogs/default/catalogTest.xml")
+                                                                                 .put("org.killbill.invoice.maxDailyNumberOfItemsSafetyBound", "30")
+                                                                                 .put("org.killbill.payment.retry.days", "8,8,8,8,8,8,8,8")
+                                                                                 .put("org.killbill.osgi.bundle.install.dir", "/var/tmp/beatrix-bundles")
+                                                                                 // The default value is 50, i.e. wait 50 x 100ms = 5s to get the lock. This isn't always enough and can lead to random tests failures
+                                                                                 // in the listener status: after moving the clock, if there are two notifications triggering an invoice run, we typically expect
+                                                                                 // both an INVOICE and a NULL_INVOICE event. If the invoice generation takes too long, the NULL_INVOICE event is never generated
+                                                                                 // (LockFailedException): the test itself doesn't fail (the correct invoice is generated), but assertListenerStatus() would.
+                                                                                 .put("org.killbill.invoice.globalLock.retries", 150)
+                                                                                 .build();
+
     @Override
     protected KillbillConfigSource getConfigSource() {
-        return getConfigSource("/beatrix.properties");
+        return getConfigSource(null, DEFAULT_BEATRIX_PROPERTIES);
     }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
index 965a5fb..149b1c1 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
@@ -40,6 +40,7 @@ import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -50,7 +51,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
 
     @Override
     protected KillbillConfigSource getConfigSource() {
-        return super.getConfigSource("/beatrixCatalogRetireElements.properties");
+        return super.getConfigSource(null, ImmutableMap.of("org.killbill.catalog.uri", "catalogs/testCatalogRetireElements"));
     }
 
     @Test(groups = "slow")
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index 822cffe..44c383e 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -311,13 +311,9 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
             return;
         }
 
+
         final InvoiceConfig defaultInvoiceConfig = new ConfigurationObjectFactory(skifeConfigSource).build(InvoiceConfig.class);
         invoiceConfig = new ConfigurableInvoiceConfig(defaultInvoiceConfig);
-        // The default value is 50, i.e. wait 50 x 100ms = 5s to get the lock. This isn't always enough and can lead to random tests failures
-        // in the listener status: after moving the clock, if there are two notifications triggering an invoice run, we typically expect
-        // both an INVOICE and a NULL_INVOICE event. If the invoice generation takes too long, the NULL_INVOICE event is never generated
-        // (LockFailedException): the test itself doesn't fail (the correct invoice is generated), but assertListenerStatus() would.
-        invoiceConfig.setMaxGlobalLockRetries(150);
         final Injector g = Guice.createInjector(Stage.PRODUCTION, new BeatrixIntegrationModule(configSource, invoiceConfig));
         g.injectMembers(this);
     }
@@ -1009,13 +1005,10 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
     static class ConfigurableInvoiceConfig implements InvoiceConfig {
 
         private final InvoiceConfig defaultInvoiceConfig;
-
-        private int maxGlobalLockRetries;
         private boolean isInvoicingSystemEnabled;
 
         public ConfigurableInvoiceConfig(final InvoiceConfig defaultInvoiceConfig) {
             this.defaultInvoiceConfig = defaultInvoiceConfig;
-            maxGlobalLockRetries = defaultInvoiceConfig.getMaxGlobalLockRetries();
             isInvoicingSystemEnabled = defaultInvoiceConfig.isInvoicingSystemEnabled();
         }
 
@@ -1071,7 +1064,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
 
         @Override
         public int getMaxGlobalLockRetries() {
-            return maxGlobalLockRetries;
+            return defaultInvoiceConfig.getMaxGlobalLockRetries();
         }
 
         @Override
@@ -1119,10 +1112,6 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
             return getItemResultBehaviorMode();
         }
 
-        public void setMaxGlobalLockRetries(final int maxGlobalLockRetries) {
-            this.maxGlobalLockRetries = maxGlobalLockRetries;
-        }
-
         public void setInvoicingSystemEnabled(final boolean invoicingSystemEnabled) {
             isInvoicingSystemEnabled = invoicingSystemEnabled;
         }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java
index cabc048..4bb7b12 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java
@@ -39,10 +39,11 @@ public class TestInvoiceNotifications extends TestIntegrationBase {
 
     @Override
     protected KillbillConfigSource getConfigSource() {
-        ImmutableMap additionalProperties = new ImmutableMap.Builder()
+
+        return getConfigSource(null, new ImmutableMap.Builder()
+                .putAll(DEFAULT_BEATRIX_PROPERTIES)
                 .put("org.killbill.invoice.dryRunNotificationSchedule", "7d")
-                .build();
-        return getConfigSource("/beatrix.properties", additionalProperties);
+                .build());
     }
 
     @Test(groups = "slow")
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
index 2c9aa27..9b5c52d 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
@@ -66,9 +66,10 @@ public class TestPublicBus extends TestIntegrationBase {
 
     @Override
     protected KillbillConfigSource getConfigSource() {
-        final ImmutableMap<String, String> additionalProperties = new ImmutableMap.Builder<String, String>().put("org.killbill.billing.util.broadcast.rate", "500ms")
-                                                                                                            .build();
-        return getConfigSource("/beatrix.properties", additionalProperties);
+        return getConfigSource(null, new ImmutableMap.Builder()
+                .putAll(DEFAULT_BEATRIX_PROPERTIES)
+                .put("org.killbill.billing.util.broadcast.rate", "500ms")
+                .build());
     }
 
     @Override
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java
index f3892bf..44476e3 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java
@@ -95,10 +95,10 @@ public class TestWithFakeKPMPlugin extends TestIntegrationBase {
 
     @Override
     protected KillbillConfigSource getConfigSource() {
-        ImmutableMap additionalProperties = new ImmutableMap.Builder()
+        return getConfigSource(null, new ImmutableMap.Builder()
+                .putAll(DEFAULT_BEATRIX_PROPERTIES)
                 .put("org.killbill.billing.util.broadcast.rate", "100ms")
-                .build();
-        return getConfigSource("/beatrix.properties", additionalProperties);
+                .build());
     }
 
     public class FakeKPMPlugin {
diff --git a/beatrix/src/test/resources/beatrixCatalogRetireElements.properties b/beatrix/src/test/resources/beatrixCatalogRetireElements.properties
index 65c1483..889acf2 100644
--- a/beatrix/src/test/resources/beatrixCatalogRetireElements.properties
+++ b/beatrix/src/test/resources/beatrixCatalogRetireElements.properties
@@ -15,6 +15,6 @@
 # under the License.
 #
 
-org.killbill.catalog.uri=retiredCatalogs
+org.killbill.catalog.uri=catalogs/testCatalogRetireElements
 org.killbill.payment.retry.days=8,8,8,8,8,8,8,8
 org.killbill.osgi.bundle.install.dir=/var/tmp/beatrix-bundles
diff --git a/beatrix/src/test/resources/catalogs/default/catalogTest.xml b/beatrix/src/test/resources/catalogs/default/catalogTest.xml
new file mode 100644
index 0000000..77a6048
--- /dev/null
+++ b/beatrix/src/test/resources/catalogs/default/catalogTest.xml
@@ -0,0 +1,1465 @@
+<?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.
+  -->
+
+<!-- Use cases covered so far: Tiered Product (Pistol/Shotgun/Assault-Rifle) 
+	Multiple changeEvent plan policies Multiple PlanAlignment (see below, trial 
+	add-on alignments and rescue discount package) Product transition rules Add 
+	on (Scopes, Hoster) Multi-pack addon (Extra-Ammo) Addon Trial aligned to 
+	base plan (holster-monthly-regular) Addon Trial aligned to creation (holster-monthly-special) 
+	Rescue discount package (assault-rifle-annual-rescue) Plan phase with a reccurring 
+	and a one off (refurbish-maintenance) Phan with more than 2 phase (gunclub 
+	discount plans) Use Cases to do: Tiered Add On Riskfree period -->
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+
+    <effectiveDate>2011-01-01T00:00:00+00:00</effectiveDate>
+    <catalogName>Firearms</catalogName>
+
+    <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
+
+    <currencies>
+        <currency>USD</currency>
+        <currency>EUR</currency>
+        <currency>GBP</currency>
+    </currencies>
+
+    <units>
+        <unit name="bullets"/>
+        <unit name="stones"/>
+    </units>
+
+    <products>
+        <product name="Knife">
+            <category>STANDALONE</category>
+        </product>
+        <product name="Blowdart">
+            <category>BASE</category>
+        </product>
+        <product name="Pistol">
+            <category>BASE</category>
+            <available>
+                <addonProduct>Cleaning</addonProduct>
+                <addonProduct>Bullets</addonProduct>
+                <addonProduct>Refurbish-Maintenance</addonProduct>
+            </available>
+        </product>
+        <product name="Shotgun">
+            <category>BASE</category>
+            <included>
+                <addonProduct>Cleaning</addonProduct>
+            </included>
+            <available>
+                <addonProduct>Telescopic-Scope</addonProduct>
+                <addonProduct>Laser-Scope</addonProduct>
+                <addonProduct>Holster</addonProduct>
+                <addonProduct>Bullets</addonProduct>
+            </available>
+        </product>
+        <product name="Assault-Rifle">
+            <category>BASE</category>
+            <included>
+                <addonProduct>Telescopic-Scope</addonProduct>
+                <addonProduct>Cleaning</addonProduct>
+            </included>
+            <available>
+                <addonProduct>Laser-Scope</addonProduct>
+                <addonProduct>Bullets</addonProduct>
+            </available>
+        </product>
+        <product name="Trebuchet">
+            <category>BASE</category>
+        </product>
+        <product name="Cleaning">
+            <category>ADD_ON</category>
+        </product>
+        <product name="Telescopic-Scope">
+            <category>ADD_ON</category>
+        </product>
+        <product name="Laser-Scope">
+            <category>ADD_ON</category>
+        </product>
+        <product name="Holster">
+            <category>ADD_ON</category>
+        </product>
+        <product name="Extra-Ammo">
+            <category>ADD_ON</category>
+        </product>
+        <product name="Refurbish-Maintenance">
+            <category>ADD_ON</category>
+        </product>
+        <product name="Bullets">
+            <category>ADD_ON</category>
+        </product>
+    </products>
+
+    <rules>
+        <changePolicy>
+            <changePolicyCase>
+                <phaseType>TRIAL</phaseType>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+            <changePolicyCase>
+                <toPriceList>rescue</toPriceList>
+                <policy>END_OF_TERM</policy>
+            </changePolicyCase>
+            <changePolicyCase>
+                <toProduct>Assault-Rifle</toProduct>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+            <changePolicyCase>
+                <fromProduct>Pistol</fromProduct>
+                <toProduct>Shotgun</toProduct>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+            <changePolicyCase>
+                <fromBillingPeriod>MONTHLY</fromBillingPeriod>
+                <toBillingPeriod>ANNUAL</toBillingPeriod>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+            <changePolicyCase>
+                <fromBillingPeriod>ANNUAL</fromBillingPeriod>
+                <toBillingPeriod>MONTHLY</toBillingPeriod>
+                <policy>END_OF_TERM</policy>
+            </changePolicyCase>
+            <changePolicyCase>
+                <policy>END_OF_TERM</policy>
+            </changePolicyCase>
+        </changePolicy>
+        <changeAlignment>
+            <changeAlignmentCase>
+                <toPriceList>rescue</toPriceList>
+                <alignment>CHANGE_OF_PLAN</alignment>
+            </changeAlignmentCase>
+            <changeAlignmentCase>
+                <fromPriceList>rescue</fromPriceList>
+                <toPriceList>rescue</toPriceList>
+                <alignment>CHANGE_OF_PRICELIST</alignment>
+            </changeAlignmentCase>
+            <changeAlignmentCase>
+                <fromPriceList>notrial</fromPriceList>
+                <toPriceList>trial</toPriceList>
+                <alignment>CHANGE_OF_PLAN</alignment>
+            </changeAlignmentCase>
+            <changeAlignmentCase>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </changeAlignmentCase>
+        </changeAlignment>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <phaseType>TRIAL</phaseType>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+            <cancelPolicyCase>
+                <policy>END_OF_TERM</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
+        <createAlignment>
+            <createAlignmentCase>
+                <product>Laser-Scope</product>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </createAlignmentCase>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+        </createAlignment>
+        <billingAlignment>
+            <billingAlignmentCase>
+                <productCategory>ADD_ON</productCategory>
+                <alignment>BUNDLE</alignment>
+            </billingAlignmentCase>
+            <billingAlignmentCase>
+                <billingPeriod>ANNUAL</billingPeriod>
+                <alignment>SUBSCRIPTION</alignment>
+            </billingAlignmentCase>
+            <billingAlignmentCase>
+                <alignment>ACCOUNT</alignment>
+            </billingAlignmentCase>
+        </billingAlignment>
+        <priceList>
+            <priceListCase>
+                <fromPriceList>rescue</fromPriceList>
+                <toPriceList>DEFAULT</toPriceList>
+            </priceListCase>
+        </priceList>
+    </rules>
+
+    <plans>
+        <plan name="knife-monthly-notrial">
+            <product>Knife</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>29.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="blowdart-monthly-notrial">
+            <product>Blowdart</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>29.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="pistol-monthly-notrial">
+            <product>Pistol</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>19.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>19.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>19.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="blowdart-monthly">
+            <product>Blowdart</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+                <phase type="DISCOUNT">
+                    <duration>
+                        <unit>MONTHS</unit>
+                        <number>6</number>
+                    </duration>
+                    <recurring>
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <recurringPrice>
+                            <price>
+                                <currency>USD</currency>
+                                <value>9.95</value>
+                            </price>
+                            <price>
+                                <currency>EUR</currency>
+                                <value>9.95</value>
+                            </price>
+                            <price>
+                                <currency>GBP</currency>
+                                <value>9.95</value>
+                            </price>
+                        </recurringPrice>
+                    </recurring>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>29.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="blowdart-monthly-trial">
+            <product>Blowdart</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>29.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="pistol-weekly">
+            <product>Pistol</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>WEEKLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>29.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="pistol-thirty-days">
+            <product>Pistol</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>THIRTY_DAYS</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>29.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="pistol-monthly">
+            <product>Pistol</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>29.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+
+        <plan name="pistol-monthly-fixedterm">
+            <product>Pistol</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="FIXEDTERM">
+                <duration>
+                    <unit>MONTHS</unit>
+                    <number>12</number>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>29.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+
+        <plan name="shotgun-monthly" prettyName="Shotgun Monthly">
+            <product>Shotgun</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </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="assault-rifle-monthly">
+            <product>Assault-Rifle</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>599.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>349.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>399.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="pistol-quarterly">
+            <product>Pistol</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>QUARTERLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>69.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>69.95</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>69.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="pistol-annual">
+            <product>Pistol</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>199.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>199.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>199.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>
+                        </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>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="assault-rifle-annual">
+            <product>Assault-Rifle</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>5999.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>3499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>3999.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="pistol-annual-gunclub-discount">
+            <product>Pistol</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+                <phase type="DISCOUNT">
+                    <duration>
+                        <unit>MONTHS</unit>
+                        <number>6</number>
+                    </duration>
+                    <recurring>
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <recurringPrice>
+                            <price>
+                                <currency>USD</currency>
+                                <value>9.95</value>
+                            </price>
+                            <price>
+                                <currency>EUR</currency>
+                                <value>9.95</value>
+                            </price>
+                            <price>
+                                <currency>GBP</currency>
+                                <value>9.95</value>
+                            </price>
+                        </recurringPrice>
+                    </recurring>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>199.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>199.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>199.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="pistol-annual-gunclub-discount-notrial">
+            <product>Pistol</product>
+            <initialPhases>
+                <phase type="DISCOUNT">
+                    <duration>
+                        <unit>MONTHS</unit>
+                        <number>6</number>
+                    </duration>
+                    <recurring>
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <recurringPrice>
+                            <price>
+                                <currency>USD</currency>
+                                <value>9.95</value>
+                            </price>
+                            <price>
+                                <currency>EUR</currency>
+                                <value>9.95</value>
+                            </price>
+                            <price>
+                                <currency>GBP</currency>
+                                <value>9.95</value>
+                            </price>
+                        </recurringPrice>
+                    </recurring>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>199.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>199.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>199.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="shotgun-annual-gunclub-discount">
+            <product>Shotgun</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+                <phase type="DISCOUNT">
+                    <duration>
+                        <unit>MONTHS</unit>
+                        <number>6</number>
+                    </duration>
+                    <recurring>
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <recurringPrice>
+                            <price>
+                                <currency>USD</currency>
+                                <value>19.95</value>
+                            </price>
+                            <price>
+                                <currency>EUR</currency>
+                                <value>49.95</value>
+                            </price>
+                            <price>
+                                <currency>GBP</currency>
+                                <value>69.95</value>
+                            </price>
+                        </recurringPrice>
+                    </recurring>
+                </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>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="assault-rifle-annual-gunclub-discount">
+            <product>Assault-Rifle</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+                <phase type="DISCOUNT">
+                    <duration>
+                        <unit>MONTHS</unit>
+                        <number>6</number>
+                    </duration>
+                    <recurring>
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <recurringPrice>
+                            <price>
+                                <currency>USD</currency>
+                                <value>99.95</value>
+                            </price>
+                            <price>
+                                <currency>EUR</currency>
+                                <value>99.95</value>
+                            </price>
+                            <price>
+                                <currency>GBP</currency>
+                                <value>99.95</value>
+                            </price>
+                        </recurringPrice>
+                    </recurring>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>5999.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>3499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>3999.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="laser-scope-monthly">
+            <product>Laser-Scope</product>
+            <initialPhases>
+                <phase type="DISCOUNT">
+                    <duration>
+                        <unit>MONTHS</unit>
+                        <number>1</number>
+                    </duration>
+                    <recurring>
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <recurringPrice>
+                            <price>
+                                <currency>USD</currency>
+                                <value>999.95</value>
+                            </price>
+                            <price>
+                                <currency>EUR</currency>
+                                <value>499.95</value>
+                            </price>
+                            <price>
+                                <currency>GBP</currency>
+                                <value>999.95</value>
+                            </price>
+                        </recurringPrice>
+                    </recurring>
+                </phase>
+            </initialPhases>
+            <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>
+            <plansAllowedInBundle>2</plansAllowedInBundle>
+        </plan>
+        <plan name="cleaning-monthly">
+            <product>Cleaning</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>2.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>1.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>0.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="telescopic-scope-monthly">
+            <product>Telescopic-Scope</product>
+            <initialPhases>
+                <phase type="DISCOUNT">
+                    <duration>
+                        <unit>MONTHS</unit>
+                        <number>1</number>
+                    </duration>
+                    <recurring>
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <recurringPrice>
+                            <price>
+                                <currency>USD</currency>
+                                <value>399.95</value>
+                            </price>
+                            <price>
+                                <currency>EUR</currency>
+                                <value>299.95</value>
+                            </price>
+                            <price>
+                                <currency>GBP</currency>
+                                <value>399.95</value>
+                            </price>
+                        </recurringPrice>
+                    </recurring>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>999.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>999.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>999.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>999.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+            <plansAllowedInBundle>-1</plansAllowedInBundle>
+            <!-- arbitrary number of these (multipack) -->
+        </plan>
+        <plan name="holster-monthly-regular">
+            <product>Holster</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>199.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>199.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>199.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="holster-monthly-special">
+            <product>Holster</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>199.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>199.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>199.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="assault-rifle-annual-rescue">
+            <product>Assault-Rifle</product>
+            <initialPhases>
+                <phase type="DISCOUNT">
+                    <duration>
+                        <unit>YEARS</unit>
+                        <number>1</number>
+                    </duration>
+                    <recurring>
+                        <billingPeriod>ANNUAL</billingPeriod>
+                        <recurringPrice>
+                            <price>
+                                <currency>USD</currency>
+                                <value>5999.95</value>
+                            </price>
+                            <price>
+                                <currency>EUR</currency>
+                                <value>3499.95</value>
+                            </price>
+                            <price>
+                                <currency>GBP</currency>
+                                <value>3999.95</value>
+                            </price>
+                        </recurringPrice>
+                    </recurring>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>ANNUAL</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>5999.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>3499.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>3999.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="refurbish-maintenance">
+            <product>Refurbish-Maintenance</product>
+            <finalPhase type="FIXEDTERM">
+                <duration>
+                    <unit>MONTHS</unit>
+                    <number>12</number>
+                </duration>
+                <fixed>
+                    <fixedPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>599.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>599.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>599.95</value>
+                        </price>
+                    </fixedPrice>
+                </fixed>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>USD</currency>
+                            <value>199.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>199.95</value>
+                        </price>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>199.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="bullets-usage-in-arrear" prettyName="Bullet Monthly Plan">
+            <product>Bullets</product>
+            <finalPhase type="EVERGREEN" prettyName="Bullet Monthly Plan Evergreen">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <usages>
+                    <usage name="bullets-usage-in-arrear-usage" prettyName="Bullet Usage In Arrear" billingMode="IN_ARREAR" usageType="CONSUMABLE">
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <tiers>
+                            <tier>
+                                <blocks>
+                                    <tieredBlock>
+                                        <unit>bullets</unit>
+                                        <size>100</size>
+                                        <prices>
+                                            <price>
+                                                <currency>USD</currency>
+                                                <value>2.95</value>
+                                            </price>
+                                            <price>
+                                                <currency>EUR</currency>
+                                                <value>1.95</value>
+                                            </price>
+                                            <price>
+                                                <currency>GBP</currency>
+                                                <value>0.95</value>
+                                            </price>
+                                        </prices>
+                                        <max>10</max>
+                                    </tieredBlock>
+                                </blocks>
+                            </tier>
+                            <tier>
+                                <blocks>
+                                    <tieredBlock>
+                                        <unit>bullets</unit>
+                                        <size>1000</size>
+                                        <prices>
+                                            <price>
+                                                <currency>USD</currency>
+                                                <value>5.95</value>
+                                            </price>
+                                            <price>
+                                                <currency>EUR</currency>
+                                                <value>4.95</value>
+                                            </price>
+                                            <price>
+                                                <currency>GBP</currency>
+                                                <value>3.95</value>
+                                            </price>
+                                        </prices>
+                                        <max>100</max>
+                                    </tieredBlock>
+                                </blocks>
+                            </tier>
+                        </tiers>
+                    </usage>
+                </usages>
+            </finalPhase>
+            <plansAllowedInBundle>-1</plansAllowedInBundle>
+            <!-- arbitrary number of these (multipack) -->
+        </plan>
+        <plan name="trebuchet-usage-in-arrear" prettyName="Trebuchet Monthly Plan">
+            <product>Trebuchet</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <usages>
+                    <usage name="trebuchet-in-arrear-usage" billingMode="IN_ARREAR" usageType="CAPACITY">
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <tiers>
+                            <tier>
+                                <limits>
+                                    <limit>
+                                        <unit>stones</unit>
+                                        <max>100</max>
+                                    </limit>
+                                </limits>
+                                <recurringPrice>
+                                    <price>
+                                        <currency>USD</currency>
+                                        <value>100</value>
+                                    </price>
+                                </recurringPrice>
+                            </tier>
+                            <tier>
+                                <limits>
+                                    <limit>
+                                        <unit>stones</unit>
+                                        <max>-1</max>
+                                    </limit>
+                                </limits>
+                                <recurringPrice>
+                                    <price>
+                                        <currency>USD</currency>
+                                        <value>1000</value>
+                                    </price>
+                                </recurringPrice>
+                            </tier>
+                        </tiers>
+                    </usage>
+                </usages>
+            </finalPhase>
+        </plan>
+    </plans>
+    <priceLists>
+        <defaultPriceList name="DEFAULT">
+            <plans>
+                <plan>pistol-weekly</plan>
+                <plan>pistol-thirty-days</plan>
+                <plan>blowdart-monthly</plan>
+                <plan>pistol-monthly</plan>
+                <plan>shotgun-monthly</plan>
+                <plan>assault-rifle-monthly</plan>
+                <plan>pistol-annual</plan>
+                <plan>pistol-quarterly</plan>
+                <plan>shotgun-annual</plan>
+                <plan>assault-rifle-annual</plan>
+                <plan>trebuchet-usage-in-arrear</plan>
+                <plan>laser-scope-monthly</plan>
+                <plan>telescopic-scope-monthly</plan>
+                <plan>cleaning-monthly</plan>
+                <plan>extra-ammo-monthly</plan>
+                <plan>holster-monthly-regular</plan>
+                <plan>refurbish-maintenance</plan>
+                <plan>bullets-usage-in-arrear</plan>
+                <plan>holster-monthly-special</plan>
+            </plans>
+        </defaultPriceList>
+        <childPriceList name="gunclubDiscount">
+            <plans>
+                <plan>pistol-annual-gunclub-discount</plan>
+                <plan>shotgun-annual-gunclub-discount</plan>
+                <plan>assault-rifle-annual-gunclub-discount</plan>
+            </plans>
+        </childPriceList>
+        <childPriceList name="gunclubDiscountNoTrial">
+            <plans>
+                <plan>pistol-annual-gunclub-discount-notrial</plan>
+            </plans>
+        </childPriceList>
+        <childPriceList name="rescue">
+            <plans>
+                <plan>assault-rifle-annual-rescue</plan>
+            </plans>
+        </childPriceList>
+        <childPriceList name="fixedTerm">
+            <plans>
+                <plan>pistol-monthly-fixedterm</plan>
+            </plans>
+        </childPriceList>
+        <childPriceList name="notrial">
+            <plans>
+                <plan>knife-monthly-notrial</plan>
+                <plan>blowdart-monthly-notrial</plan>
+                <plan>pistol-monthly-notrial</plan>
+            </plans>
+        </childPriceList>
+        <childPriceList name="trial">
+            <plans>
+                <plan>blowdart-monthly-trial</plan>
+            </plans>
+        </childPriceList>
+    </priceLists>
+
+</catalog>
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 443c8ff..264066e 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -548,7 +548,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
                                                                       // Note! The amount is negated here!
                                                                       inputItem.getAmount().negate(),
                                                                       accountCurrency,
-                                                                      inputItem.getDescription());
+                                                                      inputItem.getItemDetails());
 
                             break;
                         case TAX:
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index 5470980..fd7d11a 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -357,8 +357,9 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                                    // The restriction on the amount is to deal with https://github.com/killbill/killbill/issues/993 - and esnure that duplicate
                                    // items would not be re-written
                                    (invoiceItemModelDao.getAmount().compareTo(existingInvoiceItem.getAmount()) != 0)) {
-                            checkAgainstExistingInvoiceItemState(existingInvoiceItem, invoiceItemModelDao);
-                            transInvoiceItemSqlDao.updateItemFields(invoiceItemModelDao.getId().toString(), invoiceItemModelDao.getAmount(), invoiceItemModelDao.getDescription(), invoiceItemModelDao.getItemDetails(), context);
+                            if (checkAgainstExistingInvoiceItemState(existingInvoiceItem, invoiceItemModelDao)) {
+                                transInvoiceItemSqlDao.updateItemFields(invoiceItemModelDao.getId().toString(), invoiceItemModelDao.getAmount(), invoiceItemModelDao.getDescription(), invoiceItemModelDao.getItemDetails(), context);
+                            }
                         }
                     }
 
@@ -1321,48 +1322,6 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
     }
 
     private static boolean checkAgainstExistingInvoiceItemState(final InvoiceItemModelDao existingInvoiceItem, final InvoiceItemModelDao inputInvoiceItem) {
-        Preconditions.checkState(existingInvoiceItem.getAccountId().equals(inputInvoiceItem.getAccountId()), String.format("Unexpected account ID '%s' for invoice item '%s'",
-                                                                                                                           inputInvoiceItem.getAccountId(), existingInvoiceItem.getId()));
-        if (existingInvoiceItem.getChildAccountId() != null) {
-            Preconditions.checkState(existingInvoiceItem.getChildAccountId().equals(inputInvoiceItem.getChildAccountId()), String.format("Unexpected child account ID '%s' for invoice item '%s'",
-                                                                                                                                         inputInvoiceItem.getChildAccountId(), existingInvoiceItem.getId()));
-        }
-        Preconditions.checkState(existingInvoiceItem.getInvoiceId().equals(inputInvoiceItem.getInvoiceId()), String.format("Unexpected invoice ID '%s' for invoice item '%s'",
-                                                                                                                           inputInvoiceItem.getInvoiceId(), existingInvoiceItem.getId()));
-        if (existingInvoiceItem.getBundleId() != null) {
-            Preconditions.checkState(existingInvoiceItem.getBundleId().equals(inputInvoiceItem.getBundleId()), String.format("Unexpected bundle ID '%s' for invoice item '%s'",
-                                                                                                                             inputInvoiceItem.getBundleId(), existingInvoiceItem.getId()));
-        }
-        if (existingInvoiceItem.getSubscriptionId() != null) {
-            Preconditions.checkState(existingInvoiceItem.getSubscriptionId().equals(inputInvoiceItem.getSubscriptionId()), String.format("Unexpected subscription ID '%s' for invoice item '%s'",
-                                                                                                                                         inputInvoiceItem.getSubscriptionId(), existingInvoiceItem.getId()));
-        }
-        if (existingInvoiceItem.getPlanName() != null) {
-            Preconditions.checkState(existingInvoiceItem.getPlanName().equals(inputInvoiceItem.getPlanName()), String.format("Unexpected plan name '%s' for invoice item '%s'",
-                                                                                                                             inputInvoiceItem.getPlanName(), existingInvoiceItem.getId()));
-        }
-        if (existingInvoiceItem.getPhaseName() != null) {
-            Preconditions.checkState(existingInvoiceItem.getPhaseName().equals(inputInvoiceItem.getPhaseName()), String.format("Unexpected phase name '%s' for invoice item '%s'",
-                                                                                                                               inputInvoiceItem.getPhaseName(), existingInvoiceItem.getId()));
-        }
-        if (existingInvoiceItem.getUsageName() != null) {
-            Preconditions.checkState(existingInvoiceItem.getUsageName().equals(inputInvoiceItem.getUsageName()), String.format("Unexpected usage name '%s' for invoice item '%s'",
-                                                                                                                               inputInvoiceItem.getUsageName(), existingInvoiceItem.getId()));
-        }
-        if (existingInvoiceItem.getStartDate() != null) {
-            Preconditions.checkState(existingInvoiceItem.getStartDate().equals(inputInvoiceItem.getStartDate()), String.format("Unexpected startDate '%s' for invoice item '%s'",
-                                                                                                                               inputInvoiceItem.getStartDate(), existingInvoiceItem.getId()));
-        }
-        if (existingInvoiceItem.getEndDate() != null) {
-            Preconditions.checkState(existingInvoiceItem.getEndDate().equals(inputInvoiceItem.getEndDate()), String.format("Unexpected endDate '%s' for invoice item '%s'",
-                                                                                                                           inputInvoiceItem.getEndDate(), existingInvoiceItem.getId()));
-        }
-
-        Preconditions.checkState(existingInvoiceItem.getCurrency() == inputInvoiceItem.getCurrency(), String.format("Unexpected currency '%s' for invoice item '%s'",
-                                                                                                                    inputInvoiceItem.getCurrency(), existingInvoiceItem.getId()));
-        Preconditions.checkState(existingInvoiceItem.getType() == inputInvoiceItem.getType(), String.format("Unexpected item type '%s' for invoice item '%s'",
-                                                                                                            inputInvoiceItem.getType(), existingInvoiceItem.getId()));
-
         boolean itemShouldBeUpdated = false;
         if (inputInvoiceItem.getAmount() != null) {
             itemShouldBeUpdated = existingInvoiceItem.getAmount() == null /* unlikely */|| inputInvoiceItem.getAmount().compareTo(existingInvoiceItem.getAmount()) != 0;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index 916dbb9..30b16f9 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
@@ -362,9 +362,9 @@ public class InvoiceResource extends JaxRsResourceBase {
         final CallContext callContext = context.createCallContextWithAccountId(accountId, createdBy, reason, comment, request);
         final LocalDate inputDate;
         if (dryRunSubscriptionSpec != null) {
-            if (DryRunType.UPCOMING_INVOICE.name().equals(dryRunSubscriptionSpec.getDryRunType())) {
+            if (DryRunType.UPCOMING_INVOICE.equals(dryRunSubscriptionSpec.getDryRunType())) {
                 inputDate = null;
-            } else if (DryRunType.SUBSCRIPTION_ACTION.name().equals(dryRunSubscriptionSpec.getDryRunType()) && dryRunSubscriptionSpec.getEffectiveDate() != null) {
+            } else if (DryRunType.SUBSCRIPTION_ACTION.equals(dryRunSubscriptionSpec.getDryRunType()) && dryRunSubscriptionSpec.getEffectiveDate() != null) {
                 inputDate = dryRunSubscriptionSpec.getEffectiveDate();
             } else {
                 inputDate = toLocalDate(targetDate);
@@ -376,18 +376,18 @@ public class InvoiceResource extends JaxRsResourceBase {
         // Passing a null or empty body means we are trying to generate an invoice with a (future) targetDate
         // On the other hand if body is not null, we are attempting a dryRun subscription operation
         if (dryRunSubscriptionSpec != null && dryRunSubscriptionSpec.getDryRunAction() != null) {
-            if (SubscriptionEventType.START_BILLING.toString().equals(dryRunSubscriptionSpec.getDryRunAction())) {
+            if (SubscriptionEventType.START_BILLING.equals(dryRunSubscriptionSpec.getDryRunAction())) {
                 verifyNonNullOrEmpty(dryRunSubscriptionSpec.getProductName(), "DryRun subscription product category should be specified");
                 verifyNonNullOrEmpty(dryRunSubscriptionSpec.getBillingPeriod(), "DryRun subscription billingPeriod should be specified");
                 verifyNonNullOrEmpty(dryRunSubscriptionSpec.getProductCategory(), "DryRun subscription product category should be specified");
                 if (dryRunSubscriptionSpec.getProductCategory().equals(ProductCategory.ADD_ON)) {
                     verifyNonNullOrEmpty(dryRunSubscriptionSpec.getBundleId(), "DryRun bundle ID should be specified");
                 }
-            } else if (SubscriptionEventType.CHANGE.toString().equals(dryRunSubscriptionSpec.getDryRunAction())) {
+            } else if (SubscriptionEventType.CHANGE.equals(dryRunSubscriptionSpec.getDryRunAction())) {
                 verifyNonNullOrEmpty(dryRunSubscriptionSpec.getProductName(), "DryRun subscription product category should be specified");
                 verifyNonNullOrEmpty(dryRunSubscriptionSpec.getBillingPeriod(), "DryRun subscription billingPeriod should be specified");
                 verifyNonNullOrEmpty(dryRunSubscriptionSpec.getSubscriptionId(), "DryRun subscriptionID should be specified");
-            } else if (SubscriptionEventType.STOP_BILLING.toString().equals(dryRunSubscriptionSpec.getDryRunAction())) {
+            } else if (SubscriptionEventType.STOP_BILLING.equals(dryRunSubscriptionSpec.getDryRunAction())) {
                 verifyNonNullOrEmpty(dryRunSubscriptionSpec.getSubscriptionId(), "DryRun subscriptionID should be specified");
             }
         }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
index 8a7522c..a9a2d4c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
@@ -35,6 +35,9 @@ import javax.servlet.ServletConfig;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.WriteListener;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequestWrapper;
@@ -186,17 +189,40 @@ public class PluginResource extends JaxRsResourceBase {
                                           final HttpServletResponse response, final ServletContext servletContext,
                                           final ServletConfig servletConfig, final UriInfo uriInfo) throws ServletException, IOException {
         prepareOSGIRequest(request, servletContext, servletConfig);
-        osgiServlet.service(new OSGIServletRequestWrapper(request, inputStream, formData, uriInfo.getQueryParameters()), new OSGIServletResponseWrapper(response));
 
-        if (response.isCommitted()) {
-            if (response.getStatus() >= 400) {
-                log.warn("{} responded {}", request.getPathInfo(), response.getStatus());
-            }
-            // Jersey will want to return 204, but the servlet should have done the right thing already
-            return null;
-        } else {
-            return Response.status(response.getStatus()).build();
+        final ServletRequest req = new OSGIServletRequestWrapper(request, inputStream, formData, uriInfo.getQueryParameters());
+
+        // The real ServletOutputStream is a HttpOutput, which we don't want to give to plugins.
+        // Jooby for instance would commit the underlying HTTP channel (via ServletServletResponse#send),
+        // meaning that any further headers (e.g. Profiling) that we would add would not be returned.
+        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        final OSGIServletResponseWrapper res = new OSGIServletResponseWrapper(response,
+                                                                              new ServletOutputStream() {
+                                                                                  @Override
+                                                                                  public boolean isReady() {
+                                                                                      return true;
+                                                                                  }
+
+                                                                                  @Override
+                                                                                  public void setWriteListener(final WriteListener writeListener) {
+                                                                                      throw new UnsupportedOperationException();
+                                                                                  }
+
+                                                                                  @Override
+                                                                                  public void write(final int b) throws IOException {
+                                                                                      byteArrayOutputStream.write(b);
+                                                                                  }
+                                                                              });
+
+        osgiServlet.service(req, res);
+
+        if (response.getStatus() >= 400) {
+            log.warn("{} responded {}", request.getPathInfo(), response.getStatus());
         }
+
+        return Response.status(response.getStatus())
+                       .entity(new String(byteArrayOutputStream.toByteArray()))
+                       .build();
     }
 
     private InputStream createInputStream(final HttpServletRequest request, final MultivaluedMap<String, String> form) throws IOException {
@@ -348,8 +374,16 @@ public class PluginResource extends JaxRsResourceBase {
 
     private static final class OSGIServletResponseWrapper extends HttpServletResponseWrapper {
 
-        public OSGIServletResponseWrapper(final HttpServletResponse response) {
+        private final ServletOutputStream servletOutputStream;
+
+        public OSGIServletResponseWrapper(final HttpServletResponse response, final ServletOutputStream servletOutputStream) {
             super(response);
+            this.servletOutputStream = servletOutputStream;
+        }
+
+        @Override
+        public ServletOutputStream getOutputStream() throws IOException {
+            return servletOutputStream;
         }
     }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
index 2699bba..dc77b6c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
@@ -155,6 +155,7 @@ public class SecurityResource extends JaxRsResourceBase {
     @Produces(APPLICATION_JSON)
     @Path("/users/{username:" + ANYTHING_PATTERN + "}/roles")
     @ApiOperation(value = "Get roles associated to a user", response = UserRolesJson.class)
+    @ApiResponses(value = {@ApiResponse(code = 404, message = "The user does not exist or has been inactivated")})
     public Response getUserRoles(@PathParam("username") final String username,
                                  @javax.ws.rs.core.Context final HttpServletRequest request,
                                  @javax.ws.rs.core.Context final UriInfo uriInfo) throws SecurityApiException {

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 452a2de..4a29a27 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.141.79-SNAPSHOT</version>
+        <version>0.141.81-SNAPSHOT</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.19.17-SNAPSHOT</version>
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
index 62bb358..762437d 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
@@ -47,6 +47,7 @@ import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementSourceType;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseApiService;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
@@ -580,8 +581,18 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
 
 
     public boolean isPendingChangePlan() {
-        final SubscriptionBaseTransition pendingTransition = getPendingTransition();
-        return pendingTransition != null && pendingTransition.getTransitionType() == SubscriptionBaseTransitionType.CHANGE;
+
+        final SubscriptionBaseTransitionDataIterator it = new SubscriptionBaseTransitionDataIterator(
+                clock, transitions, Order.ASC_FROM_PAST,
+                Visibility.ALL, TimeLimit.FUTURE_ONLY);
+
+        while (it.hasNext()) {
+            final SubscriptionBaseTransition next = it.next();
+            if (next.getTransitionType() == SubscriptionBaseTransitionType.CHANGE) {
+                return true;
+            }
+        }
+        return false;
     }
 
 
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java
index 31a211e..9b4c902 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java
@@ -613,4 +613,57 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(refreshedSubscription.getAllTransitions().get(1).getTransitionType(), SubscriptionBaseTransitionType.PHASE);
 
     }
+
+    @Test(groups = "slow")
+    public void testUndoChangePlanOnPendingSubscription() throws SubscriptionBaseApiException {
+
+
+        final DateTime futureStartDate = clock.getUTCNow().plusDays(7);
+        final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, "Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null, internalCallContext.toLocalDate(futureStartDate));
+        assertNotNull(subscription);
+
+        clock.addDays(1);
+
+        // Change plan in the future
+        final DateTime futureChangeDate = futureStartDate.plusDays(5);
+        subscription.changePlanWithDate(new PlanPhaseSpecifier("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), null, futureChangeDate, callContext);
+        assertListenerStatus();
+
+        DefaultSubscriptionBase refreshedSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
+        assertEquals(refreshedSubscription.getAllTransitions().size(), 3);
+        assertEquals(refreshedSubscription.getAllTransitions().get(0).getTransitionType(), SubscriptionBaseTransitionType.CREATE);
+        assertEquals(refreshedSubscription.getAllTransitions().get(1).getTransitionType(), SubscriptionBaseTransitionType.CHANGE);
+        assertEquals(refreshedSubscription.getAllTransitions().get(2).getTransitionType(), SubscriptionBaseTransitionType.PHASE);
+        assertEquals(refreshedSubscription.getAllTransitions().get(2).getNextPlan().getName(), "pistol-monthly");
+
+        clock.addDays(1);
+
+        testListener.pushExpectedEvent(NextEvent.UNDO_CHANGE);
+        subscription.undoChangePlan(callContext);
+        assertListenerStatus();
+
+        testListener.pushExpectedEvent(NextEvent.CREATE);
+        clock.addDays(5);
+        assertListenerStatus();
+
+        // No CHANGE_PLAN
+        clock.addDays(5);
+        assertListenerStatus();
+
+
+        // Verify PHASE event for Shotgun is active
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        clock.addDays(25);
+        assertListenerStatus();
+
+
+        refreshedSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
+        assertEquals(refreshedSubscription.getAllTransitions().size(), 2);
+        assertEquals(refreshedSubscription.getAllTransitions().get(0).getTransitionType(), SubscriptionBaseTransitionType.CREATE);
+        assertEquals(refreshedSubscription.getAllTransitions().get(1).getTransitionType(), SubscriptionBaseTransitionType.PHASE);
+        assertEquals(refreshedSubscription.getAllTransitions().get(1).getNextPlan().getName(), "shotgun-monthly");
+
+    }
+
+
 }
diff --git a/util/src/main/java/org/killbill/billing/util/security/api/DefaultSecurityApi.java b/util/src/main/java/org/killbill/billing/util/security/api/DefaultSecurityApi.java
index 3d30a3c..33b4ed5 100644
--- a/util/src/main/java/org/killbill/billing/util/security/api/DefaultSecurityApi.java
+++ b/util/src/main/java/org/killbill/billing/util/security/api/DefaultSecurityApi.java
@@ -188,7 +188,7 @@ public class DefaultSecurityApi implements SecurityApi {
     }
 
     @Override
-    public List<String> getUserRoles(final String username, final TenantContext tenantContext) {
+    public List<String> getUserRoles(final String username, final TenantContext tenantContext) throws SecurityApiException {
         final List<UserRolesModelDao> permissionsModelDao = userDao.getUserRoles(username);
         return ImmutableList.copyOf(Iterables.transform(permissionsModelDao, new Function<UserRolesModelDao, String>() {
             @Nullable
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/DefaultUserDao.java b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/DefaultUserDao.java
index 8f02fe2..82bd893 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/DefaultUserDao.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/DefaultUserDao.java
@@ -17,11 +17,8 @@
 
 package org.killbill.billing.util.security.shiro.dao;
 
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
-import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import org.apache.shiro.crypto.RandomNumberGenerator;
@@ -34,8 +31,6 @@ import org.killbill.billing.security.SecurityApiException;
 import org.killbill.billing.util.config.definition.SecurityConfig;
 import org.killbill.billing.util.security.shiro.KillbillCredentialsMatcher;
 import org.killbill.clock.Clock;
-import org.killbill.commons.jdbi.mapper.LowerToCamelBeanMapperFactory;
-import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.Handle;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.TransactionCallback;
@@ -85,10 +80,17 @@ public class DefaultUserDao implements UserDao {
         });
     }
 
-    public List<UserRolesModelDao> getUserRoles(final String username) {
-        return dbi.inTransaction(new TransactionCallback<List<UserRolesModelDao>>() {
+    @Override
+    public List<UserRolesModelDao> getUserRoles(final String username) throws SecurityApiException {
+        return inTransactionWithExceptionHandling(new TransactionCallback<List<UserRolesModelDao>>() {
             @Override
             public List<UserRolesModelDao> inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
+                final UsersSqlDao usersSqlDao = handle.attach(UsersSqlDao.class);
+                final UserModelDao userModelDao = usersSqlDao.getByUsername(username);
+                if (userModelDao == null) {
+                    throw new SecurityApiException(ErrorCode.SECURITY_INVALID_USER, username);
+                }
+
                 final UserRolesSqlDao userRolesSqlDao = handle.attach(UserRolesSqlDao.class);
                 return userRolesSqlDao.getByUsername(username);
             }
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/UserDao.java b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/UserDao.java
index f0b4427..5e80394 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/UserDao.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/UserDao.java
@@ -25,7 +25,7 @@ public interface UserDao {
 
     public void insertUser(String username, String password, List<String> roles, String createdBy) throws SecurityApiException;
 
-    public List<UserRolesModelDao> getUserRoles(String username);
+    public List<UserRolesModelDao> getUserRoles(String username) throws SecurityApiException;
 
     public void addRoleDefinition(String role, List<String> permissions, String createdBy) throws SecurityApiException;