killbill-memoizeit

Merge pull request #1117 from killbill/fix-for-1110-master Major

3/11/2019 5:22:50 PM

Changes

Details

diff --git a/api/src/main/java/org/killbill/billing/junction/BillingEvent.java b/api/src/main/java/org/killbill/billing/junction/BillingEvent.java
index 9588349..758dde7 100644
--- a/api/src/main/java/org/killbill/billing/junction/BillingEvent.java
+++ b/api/src/main/java/org/killbill/billing/junction/BillingEvent.java
@@ -20,6 +20,7 @@ package org.killbill.billing.junction;
 
 import java.math.BigDecimal;
 import java.util.List;
+import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.killbill.billing.catalog.api.BillingAlignment;
@@ -34,6 +35,11 @@ import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 
 public interface BillingEvent extends Comparable<BillingEvent> {
 
+    UUID getSubscriptionId();
+
+
+    UUID getBundleId();
+
     /**
      * @return the billCycleDay in the account timezone as seen for that subscription at that time
      * <p/>
@@ -46,10 +52,6 @@ public interface BillingEvent extends Comparable<BillingEvent> {
      */
     BillingAlignment getBillingAlignment();
 
-    /**
-     * @return the subscription
-     */
-    SubscriptionBase getSubscription();
 
     /**
      * @return the date for when that event became effective
@@ -84,16 +86,19 @@ public interface BillingEvent extends Comparable<BillingEvent> {
     /**
      * @return the recurring price for the phase
      */
-    BigDecimal getRecurringPrice(DateTime effectiveDate) throws CatalogApiException;
+    BigDecimal getRecurringPrice(DateTime requestedDate) throws CatalogApiException;
 
     /**
      * @return the currency for the account being invoiced
      */
     Currency getCurrency();
 
-    /**
-     * @return the transition type of the underlying subscription event that triggered this
-     */
+
+    public DateTime getLastChangePlanDate();
+
+        /**
+         * @return the transition type of the underlying subscription event that triggered this
+         */
     SubscriptionBaseTransitionType getTransitionType();
 
     /**
@@ -104,7 +109,7 @@ public interface BillingEvent extends Comparable<BillingEvent> {
     /**
      * @return the list of {@code Usage} section
      */
-    List<Usage> getUsages();
+    List<Usage> getUsages(/*DateTime requestedDate*/) throws CatalogApiException;
 
     /**
      *
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
index 562aa85..4da292a 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
@@ -31,12 +31,11 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
-import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
-import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.entitlement.api.EntitlementAOStatusDryRun;
 import org.killbill.billing.entitlement.api.EntitlementSpecifier;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
 import org.killbill.billing.invoice.api.DryRunArguments;
+import org.killbill.billing.subscription.api.user.SubscriptionBillingEvent;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.util.entity.Pagination;
@@ -83,7 +82,7 @@ public interface SubscriptionBaseInternalApi {
 
     public List<EffectiveSubscriptionInternalEvent> getAllTransitions(SubscriptionBase subscription, InternalTenantContext context);
 
-    public List<EffectiveSubscriptionInternalEvent> getBillingTransitions(SubscriptionBase subscription, InternalTenantContext context);
+    public List<SubscriptionBillingEvent> getSubscriptionBillingEvents(SubscriptionBase subscription, InternalTenantContext context);
 
     public DateTime getDryRunChangePlanEffectiveDate(SubscriptionBase subscription, EntitlementSpecifier spec, DateTime requestedDate, BillingActionPolicy policy, InternalCallContext context) throws SubscriptionBaseApiException, CatalogApiException;
 
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBillingEvent.java b/api/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBillingEvent.java
new file mode 100644
index 0000000..43719a9
--- /dev/null
+++ b/api/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBillingEvent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014-2019 Groupon, Inc
+ * Copyright 2014-2019 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.subscription.api.user;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
+
+public interface SubscriptionBillingEvent {
+
+    String getPlanName();
+
+    String getPlanPhaseName();
+
+    DateTime getEffectiveDate();
+
+    Long getTotalOrdering();
+
+    SubscriptionBaseTransitionType getType();
+
+    DateTime getLastChangePlanDate();
+
+    Integer getBcdLocal();
+
+}
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 80fb354..d347a18 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
@@ -17,6 +17,7 @@
 
 package org.killbill.billing.beatrix.integration;
 
+import java.math.BigDecimal;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -26,6 +27,7 @@ import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
@@ -37,6 +39,7 @@ import org.killbill.billing.entitlement.api.Entitlement;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.entitlement.api.EntitlementApiException;
 import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.testng.annotations.Test;
@@ -78,7 +81,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
         assertListenerStatus();
         Entitlement bpEntitlement = entitlementApi.getEntitlementForId(bpEntitlementId, callContext);
 
-        // Move out a month. Date > caralog V2
+        // Move out a month. Date > catalog V2
         busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addMonths(1);
         assertListenerStatus();
@@ -91,11 +94,20 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
         assertListenerStatus();
 
 
-        // Change back to original plan -- because we use the latest catalog version the change plan succeeds
+        // Change back to original plan: The code (subscription) chooses the latest version of the catalog when making the change and therefore the call succeeds
         busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE);
         bpEntitlement.changePlanWithDate(new DefaultEntitlementSpecifier(spec1), clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
+        //
+        // The code normally goes through the grandfathering logic to find the version but specifies the transitionTime of the latest CHANGE (and not the subscriptionStartDate)
+        // and therefore correctly find the latest catalog version, invoicing at the new price 295.95
+        //
+        invoiceChecker.checkInvoice(account.getId(), 4, callContext,
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2015, 12, 5), new LocalDate(2016, 1, 5), InvoiceItemType.RECURRING, new BigDecimal("295.95")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2015, 12, 5), new LocalDate(2016, 1, 5), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-500.00")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2015, 12, 5), new LocalDate(2015, 12, 5), InvoiceItemType.CBA_ADJ, new BigDecimal("204.05")));
+
     }
 
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogWithEffectiveDateForExistingSubscriptions.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogWithEffectiveDateForExistingSubscriptions.java
new file mode 100644
index 0000000..738c47d
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogWithEffectiveDateForExistingSubscriptions.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2014-2019 Groupon, Inc
+ * Copyright 2014-2019 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.beatrix.integration;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.api.DefaultEntitlement;
+import org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.testng.Assert.assertNotNull;
+
+public class TestCatalogWithEffectiveDateForExistingSubscriptions extends TestIntegrationBase {
+
+    @Override
+    protected KillbillConfigSource getConfigSource(final Map<String, String> extraProperties) {
+        final Map<String, String> allExtraProperties = new HashMap<String, String>(extraProperties);
+        allExtraProperties.put("org.killbill.catalog.uri", "catalogs/testCatalogWithEffectiveDateForExistingSubscriptions");
+        return super.getConfigSource(null, allExtraProperties);
+    }
+
+    @Test(groups = "slow")
+    public void testRecurringPlan() throws Exception {
+
+        final LocalDate today = new LocalDate(2018, 1, 1);
+        clock.setDay(today);
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
+        final DefaultEntitlement bpEntitlement =
+                createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey1", "PlumberInsurance",
+                                                           ProductCategory.BASE, BillingPeriod.MONTHLY,
+                                                           NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+
+        assertNotNull(bpEntitlement);
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext,
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2018, 1, 1), new LocalDate(2018, 2, 1), InvoiceItemType.RECURRING, new BigDecimal("49.95")));
+
+        // Catalog v2 with price increase is on 2018-04-01 but because we have an effectiveDateForExistingSubscriptions set to 2018-05-01
+        // we don't see any change until 5-1
+        //
+        // 2018-2-1
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext,
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2018, 2, 1), new LocalDate(2018, 3, 1), InvoiceItemType.RECURRING, new BigDecimal("49.95")));
+
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        // 2018-3-1
+        clock.addMonths(1);
+        assertListenerStatus();
+        invoiceChecker.checkInvoice(account.getId(), 3, callContext,
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2018, 3, 1), new LocalDate(2018, 4, 1), InvoiceItemType.RECURRING, new BigDecimal("49.95")));
+
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        // 2018-4-1
+        clock.addMonths(1);
+        assertListenerStatus();
+        invoiceChecker.checkInvoice(account.getId(), 4, callContext,
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2018, 4, 1), new LocalDate(2018, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("49.95")));
+
+
+        // effectiveDateForExistingSubscriptions set to 2018-05-01 should kick-in and we should see the price increase
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        // 2018-5-1
+        clock.addMonths(1);
+        assertListenerStatus();
+        invoiceChecker.checkInvoice(account.getId(), 5, callContext,
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2018, 5, 1), new LocalDate(2018, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("59.95")));
+
+
+        // One more month to make sure
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        // 2018-6-1
+        clock.addMonths(1);
+        assertListenerStatus();
+        invoiceChecker.checkInvoice(account.getId(), 6, callContext,
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2018, 6, 1), new LocalDate(2018, 7, 1), InvoiceItemType.RECURRING, new BigDecimal("59.95")));
+
+
+        // Catalog v3 with price increase is on 2018-07-01 but because we have an effectiveDateForExistingSubscriptions set to 2018-08-01
+        // we don't see any change until 8-1
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        // 2018-7-1
+        clock.addMonths(1);
+        assertListenerStatus();
+        invoiceChecker.checkInvoice(account.getId(), 7, callContext,
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2018, 7, 1), new LocalDate(2018, 8, 1), InvoiceItemType.RECURRING, new BigDecimal("59.95")));
+
+
+        // Check we see the new price for catalog version v3
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        // 2018-8-1
+        clock.addMonths(1);
+        assertListenerStatus();
+        invoiceChecker.checkInvoice(account.getId(), 8, callContext,
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2018, 8, 1), new LocalDate(2018, 9, 1), InvoiceItemType.RECURRING, new BigDecimal("69.95")));
+
+
+
+    }
+
+    @Test(groups = "slow")
+    public void testUsagePlan() throws Exception {
+
+        final LocalDate today = new LocalDate(2018, 1, 1);
+        clock.setDay(today);
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("water-monthly");
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
+        final UUID entitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec), null, null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+    }
+
+}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
index f8cf109..14f7fd9 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
@@ -222,6 +222,7 @@ public class TestWithCatalogPlugin extends TestIntegrationBase {
             return new TestModelVersionedPluginCatalog(versionedCatalog.getCatalogName(), toStandalonePluginCatalogs(versionedCatalog.getVersions()));
         }
 
+        // This actually pulls catalog resources from `catalog` module and not the one from beatrix/src/test/resources//catalogs
         public void addCatalogVersion(final String catalogResource) throws Exception {
 
             final StandaloneCatalog inputCatalogVersion = XMLLoader.getObjectFromString(Resources.getResource(catalogResource).toExternalForm(), StandaloneCatalog.class);
diff --git a/beatrix/src/test/resources/catalogs/testCatalogRetireElements/WeaponsHireSmall-v1.xml b/beatrix/src/test/resources/catalogs/testCatalogRetireElements/WeaponsHireSmall-v1.xml
index 9cd2a82..e79acf8 100644
--- a/beatrix/src/test/resources/catalogs/testCatalogRetireElements/WeaponsHireSmall-v1.xml
+++ b/beatrix/src/test/resources/catalogs/testCatalogRetireElements/WeaponsHireSmall-v1.xml
@@ -109,7 +109,6 @@
 
     <plans>
         <plan name="pistol-monthly">
-            <effectiveDateForExistingSubscriptions>2011-03-14T00:00:00+00:00</effectiveDateForExistingSubscriptions>
             <product>Pistol</product>
             <initialPhases>
                 <phase type="TRIAL">
diff --git a/beatrix/src/test/resources/catalogs/testCatalogRetireElements/WeaponsHireSmall-v2.xml b/beatrix/src/test/resources/catalogs/testCatalogRetireElements/WeaponsHireSmall-v2.xml
index d9fe488..82649c1 100644
--- a/beatrix/src/test/resources/catalogs/testCatalogRetireElements/WeaponsHireSmall-v2.xml
+++ b/beatrix/src/test/resources/catalogs/testCatalogRetireElements/WeaponsHireSmall-v2.xml
@@ -195,15 +195,15 @@
                     <recurringPrice>
                         <price>
                             <currency>USD</currency>
-                            <value>249.95</value>
+                            <value>295.95</value>
                         </price>
                         <price>
                             <currency>EUR</currency>
-                            <value>149.95</value>
+                            <value>249.95</value>
                         </price>
                         <price>
                             <currency>GBP</currency>
-                            <value>169.95</value>
+                            <value>269.95</value>
                         </price>
                     </recurringPrice>
                 </recurring>
diff --git a/beatrix/src/test/resources/catalogs/testCatalogWithEffectiveDateForExistingSubscriptions/WaterUtility-v1.xml b/beatrix/src/test/resources/catalogs/testCatalogWithEffectiveDateForExistingSubscriptions/WaterUtility-v1.xml
new file mode 100644
index 0000000..cea38e8
--- /dev/null
+++ b/beatrix/src/test/resources/catalogs/testCatalogWithEffectiveDateForExistingSubscriptions/WaterUtility-v1.xml
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ Copyright 2014 The Billing Project, Inc.
+  ~
+  ~ Ning 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.
+  -->
+
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+
+    <effectiveDate>2018-01-01T00:00:00+00:00</effectiveDate>
+    <catalogName>MyCatalog</catalogName>
+
+    <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
+
+    <currencies>
+        <currency>USD</currency>
+    </currencies>
+
+    <units>
+        <unit name="liter"/>
+    </units>
+
+    <products>
+        <product name="PlumberInsurance">
+            <category>BASE</category>
+        </product>
+        <product name="Water">
+            <category>BASE</category>
+        </product>
+    </products>
+
+    <rules>
+        <changePolicy>
+            <changePolicyCase>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+        </changePolicy>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
+    </rules>
+
+    <plans>
+        <plan name="plumber-insurance-monthly-no-trial">
+            <product>PlumberInsurance</product>
+            <initialPhases>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>49.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>49.95</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>49.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+
+
+        <!-- pure usage plan   -->
+        <plan name="water-monthly">
+            <product>Water</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <usages>
+                    <usage name="water-monthly-usage" billingMode="IN_ARREAR" usageType="CONSUMABLE" tierBlockPolicy="ALL_TIERS">
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <tiers>
+                            <tier>
+                                <blocks>
+                                    <tieredBlock>
+                                        <unit>liter</unit>
+                                        <size>1</size>
+                                        <prices>
+                                            <price>
+                                                <currency>USD</currency>
+                                                <value>1.50</value>
+                                            </price>
+                                        </prices>
+                                        <max>1000</max>
+                                    </tieredBlock>
+                                </blocks>
+                            </tier>
+                            <tier>
+                                <blocks>
+                                    <tieredBlock>
+                                        <unit>liter</unit>
+                                        <size>1</size>
+                                        <prices>
+                                            <price>
+                                                <currency>USD</currency>
+                                                <value>2.00</value>
+                                            </price>
+                                        </prices>
+                                        <max>-1</max>
+                                    </tieredBlock>
+                                </blocks>
+                            </tier>
+                        </tiers>
+                    </usage>
+                </usages>
+            </finalPhase>
+        </plan>
+    </plans>
+    <priceLists>
+        <defaultPriceList name="DEFAULT">
+            <plans>
+                <plan>plumber-insurance-monthly-no-trial</plan>
+                <plan>water-monthly</plan>
+            </plans>
+        </defaultPriceList>
+    </priceLists>
+</catalog>
diff --git a/beatrix/src/test/resources/catalogs/testCatalogWithEffectiveDateForExistingSubscriptions/WaterUtility-v2.xml b/beatrix/src/test/resources/catalogs/testCatalogWithEffectiveDateForExistingSubscriptions/WaterUtility-v2.xml
new file mode 100644
index 0000000..c6b7481
--- /dev/null
+++ b/beatrix/src/test/resources/catalogs/testCatalogWithEffectiveDateForExistingSubscriptions/WaterUtility-v2.xml
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ Copyright 2014 The Billing Project, Inc.
+  ~
+  ~ Ning 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.
+  -->
+
+<!--
+  Price increase for both plans and effectiveDateForExistingSubscriptions set to 1 month after catalog effective date
+  -->
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+
+    <effectiveDate>2018-04-01T00:00:00+00:00</effectiveDate>
+    <catalogName>MyCatalog</catalogName>
+
+    <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
+
+    <currencies>
+        <currency>USD</currency>
+    </currencies>
+
+    <units>
+        <unit name="liter"/>
+    </units>
+
+    <products>
+        <product name="PlumberInsurance">
+            <category>BASE</category>
+        </product>
+        <product name="Water">
+            <category>BASE</category>
+        </product>
+    </products>
+
+    <rules>
+        <changePolicy>
+            <changePolicyCase>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+        </changePolicy>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
+    </rules>
+
+    <plans>
+        <plan name="plumber-insurance-monthly-no-trial">
+            <effectiveDateForExistingSubscriptions>2018-05-01T00:00:00+00:00</effectiveDateForExistingSubscriptions>
+            <product>PlumberInsurance</product>
+            <initialPhases>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>59.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>59.95</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>59.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+
+
+        <!-- pure usage plan   -->
+        <plan name="water-monthly">
+            <effectiveDateForExistingSubscriptions>2018-05-01T00:00:00+00:00</effectiveDateForExistingSubscriptions>
+            <product>Water</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <usages>
+                    <usage name="water-monthly-usage" billingMode="IN_ARREAR" usageType="CONSUMABLE" tierBlockPolicy="ALL_TIERS">
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <tiers>
+                            <tier>
+                                <blocks>
+                                    <tieredBlock>
+                                        <unit>liter</unit>
+                                        <size>1</size>
+                                        <prices>
+                                            <price>
+                                                <currency>USD</currency>
+                                                <value>2.50</value>
+                                            </price>
+                                        </prices>
+                                        <max>1000</max>
+                                    </tieredBlock>
+                                </blocks>
+                            </tier>
+                            <tier>
+                                <blocks>
+                                    <tieredBlock>
+                                        <unit>liter</unit>
+                                        <size>1</size>
+                                        <prices>
+                                            <price>
+                                                <currency>USD</currency>
+                                                <value>3.00</value>
+                                            </price>
+                                        </prices>
+                                        <max>-1</max>
+                                    </tieredBlock>
+                                </blocks>
+                            </tier>
+                        </tiers>
+                    </usage>
+                </usages>
+            </finalPhase>
+        </plan>
+    </plans>
+    <priceLists>
+        <defaultPriceList name="DEFAULT">
+            <plans>
+                <plan>plumber-insurance-monthly-no-trial</plan>
+                <plan>water-monthly</plan>
+            </plans>
+        </defaultPriceList>
+    </priceLists>
+</catalog>
diff --git a/beatrix/src/test/resources/catalogs/testCatalogWithEffectiveDateForExistingSubscriptions/WaterUtility-v3.xml b/beatrix/src/test/resources/catalogs/testCatalogWithEffectiveDateForExistingSubscriptions/WaterUtility-v3.xml
new file mode 100644
index 0000000..4fd6882
--- /dev/null
+++ b/beatrix/src/test/resources/catalogs/testCatalogWithEffectiveDateForExistingSubscriptions/WaterUtility-v3.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ Copyright 2014-2019 Groupon, Inc
+  ~ Copyright 2014-2019 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.
+  -->
+
+<!--
+  Price increase for both plans and effectiveDateForExistingSubscriptions set to 1 month after catalog effective date
+  -->
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+
+    <effectiveDate>2018-07-01T00:00:00+00:00</effectiveDate>
+    <catalogName>MyCatalog</catalogName>
+
+    <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
+
+    <currencies>
+        <currency>USD</currency>
+    </currencies>
+
+    <units>
+        <unit name="liter"/>
+    </units>
+
+    <products>
+        <product name="PlumberInsurance">
+            <category>BASE</category>
+        </product>
+        <product name="Water">
+            <category>BASE</category>
+        </product>
+    </products>
+
+    <rules>
+        <changePolicy>
+            <changePolicyCase>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+        </changePolicy>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
+    </rules>
+
+    <plans>
+        <plan name="plumber-insurance-monthly-no-trial">
+            <effectiveDateForExistingSubscriptions>2018-08-01T00:00:00+00:00</effectiveDateForExistingSubscriptions>
+            <product>PlumberInsurance</product>
+            <initialPhases>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</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>
+
+
+        <!-- pure usage plan   -->
+        <plan name="water-monthly">
+            <effectiveDateForExistingSubscriptions>2018-08-01T00:00:00+00:00</effectiveDateForExistingSubscriptions>
+            <product>Water</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <usages>
+                    <usage name="water-monthly-usage" billingMode="IN_ARREAR" usageType="CONSUMABLE" tierBlockPolicy="ALL_TIERS">
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <tiers>
+                            <tier>
+                                <blocks>
+                                    <tieredBlock>
+                                        <unit>liter</unit>
+                                        <size>1</size>
+                                        <prices>
+                                            <price>
+                                                <currency>USD</currency>
+                                                <value>3.50</value>
+                                            </price>
+                                        </prices>
+                                        <max>1000</max>
+                                    </tieredBlock>
+                                </blocks>
+                            </tier>
+                            <tier>
+                                <blocks>
+                                    <tieredBlock>
+                                        <unit>liter</unit>
+                                        <size>1</size>
+                                        <prices>
+                                            <price>
+                                                <currency>USD</currency>
+                                                <value>4.00</value>
+                                            </price>
+                                        </prices>
+                                        <max>-1</max>
+                                    </tieredBlock>
+                                </blocks>
+                            </tier>
+                        </tiers>
+                    </usage>
+                </usages>
+            </finalPhase>
+        </plan>
+    </plans>
+    <priceLists>
+        <defaultPriceList name="DEFAULT">
+            <plans>
+                <plan>plumber-insurance-monthly-no-trial</plan>
+                <plan>water-monthly</plan>
+            </plans>
+        </defaultPriceList>
+    </priceLists>
+</catalog>
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultVersionedCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultVersionedCatalog.java
index 4eb9ff3..4d76881 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultVersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultVersionedCatalog.java
@@ -124,7 +124,7 @@ public class DefaultVersionedCatalog extends ValidatingConfig<DefaultVersionedCa
 
     private CatalogPlanEntry findCatalogPlanEntry(final PlanRequestWrapper wrapper,
                                                   final DateTime requestedDate,
-                                                  final DateTime subscriptionStartDate) throws CatalogApiException {
+                                                  final DateTime subscriptionChangePlanDate) throws CatalogApiException {
         final List<StandaloneCatalog> catalogs = versionsBeforeDate(requestedDate.toDate());
         if (catalogs.isEmpty()) {
             throw new CatalogApiException(ErrorCode.CAT_NO_CATALOG_FOR_GIVEN_DATE, requestedDate.toDate().toString());
@@ -149,14 +149,14 @@ public class DefaultVersionedCatalog extends ValidatingConfig<DefaultVersionedCa
 
             final boolean oldestCatalog = (i == 0);
             final DateTime catalogEffectiveDate = CatalogDateHelper.toUTCDateTime(c.getEffectiveDate());
-            final boolean catalogOlderThanSubscriptionStartDate = !subscriptionStartDate.isBefore(catalogEffectiveDate);
+            final boolean catalogOlderThanSubscriptionChangePlanDate = !subscriptionChangePlanDate.isBefore(catalogEffectiveDate);
             if (oldestCatalog || // Prevent issue with time granularity -- see #760
-                catalogOlderThanSubscriptionStartDate) { // It's a new subscription, this plan always applies
+                catalogOlderThanSubscriptionChangePlanDate) { // It's a new subscription, this plan always applies
                 return new CatalogPlanEntry(c, plan);
             } else { // It's an existing subscription
                 if (plan.getEffectiveDateForExistingSubscriptions() != null) { // If it is null, any change to this catalog does not apply to existing subscriptions
                     final DateTime existingSubscriptionDate = CatalogDateHelper.toUTCDateTime(plan.getEffectiveDateForExistingSubscriptions());
-                    if (requestedDate.isAfter(existingSubscriptionDate)) { // This plan is now applicable to existing subs
+                    if (requestedDate.compareTo(existingSubscriptionDate) >= 0) { // This plan is now applicable to existing subs
                         return new CatalogPlanEntry(c, plan);
                     }
                 } else if (candidateInSubsequentCatalog == null) {
@@ -248,9 +248,9 @@ public class DefaultVersionedCatalog extends ValidatingConfig<DefaultVersionedCa
     @Override
     public Plan findPlan(final String name,
                          final DateTime requestedDate,
-                         final DateTime subscriptionStartDate)
+                         final DateTime subscriptionChangePlanDate)
             throws CatalogApiException {
-        final CatalogPlanEntry entry = findCatalogPlanEntry(new PlanRequestWrapper(name), requestedDate, subscriptionStartDate);
+        final CatalogPlanEntry entry = findCatalogPlanEntry(new PlanRequestWrapper(name), requestedDate, subscriptionChangePlanDate);
         return entry.getPlan();
     }
 
@@ -258,9 +258,9 @@ public class DefaultVersionedCatalog extends ValidatingConfig<DefaultVersionedCa
     public Plan createOrFindPlan(final PlanSpecifier spec,
                                  final PlanPhasePriceOverridesWithCallContext overrides,
                                  final DateTime requestedDate,
-                                 final DateTime subscriptionStartDate)
+                                 final DateTime subscriptionChangePlanDate)
             throws CatalogApiException {
-        final CatalogPlanEntry entry = findCatalogPlanEntry(new PlanRequestWrapper(spec, overrides), requestedDate, subscriptionStartDate);
+        final CatalogPlanEntry entry = findCatalogPlanEntry(new PlanRequestWrapper(spec, overrides), requestedDate, subscriptionChangePlanDate);
         return entry.getPlan();
     }
 
@@ -272,43 +272,43 @@ public class DefaultVersionedCatalog extends ValidatingConfig<DefaultVersionedCa
     @Override
     public PlanPhase findPhase(final String phaseName,
                                final DateTime requestedDate,
-                               final DateTime subscriptionStartDate)
+                               final DateTime subscriptionChangePlanDate)
             throws CatalogApiException {
         final String planName = DefaultPlanPhase.planName(phaseName);
-        final Plan plan = findPlan(planName, requestedDate, subscriptionStartDate);
+        final Plan plan = findPlan(planName, requestedDate, subscriptionChangePlanDate);
         return plan.findPhase(phaseName);
     }
 
     @Override
     public PriceList findPriceListForPlan(final String planName,
                                           final DateTime requestedDate,
-                                          final DateTime subscriptionStartDate)
+                                          final DateTime subscriptionChangePlanDate)
             throws CatalogApiException {
-        final CatalogPlanEntry entry = findCatalogPlanEntry(new PlanRequestWrapper(planName), requestedDate, subscriptionStartDate);
+        final CatalogPlanEntry entry = findCatalogPlanEntry(new PlanRequestWrapper(planName), requestedDate, subscriptionChangePlanDate);
         return entry.getStaticCatalog().findCurrentPricelist(entry.getPlan().getPriceListName());
     }
 
     @Override
     public BillingActionPolicy planCancelPolicy(final PlanPhaseSpecifier planPhase,
                                                 final DateTime requestedDate,
-                                                final DateTime subscriptionStartDate) throws CatalogApiException {
-        final StaticCatalog staticCatalog = getStaticCatalog(planPhase, requestedDate, subscriptionStartDate);
+                                                final DateTime subscriptionChangePlanDate) throws CatalogApiException {
+        final StaticCatalog staticCatalog = getStaticCatalog(planPhase, requestedDate, subscriptionChangePlanDate);
         return staticCatalog.planCancelPolicy(planPhase);
     }
 
     @Override
     public PlanAlignmentCreate planCreateAlignment(final PlanSpecifier specifier,
                                                    final DateTime requestedDate,
-                                                   final DateTime subscriptionStartDate) throws CatalogApiException {
-        final StaticCatalog staticCatalog = getStaticCatalog(specifier, requestedDate, subscriptionStartDate);
+                                                   final DateTime subscriptionChangePlanDate) throws CatalogApiException {
+        final StaticCatalog staticCatalog = getStaticCatalog(specifier, requestedDate, subscriptionChangePlanDate);
         return staticCatalog.planCreateAlignment(specifier);
     }
 
     @Override
     public BillingAlignment billingAlignment(final PlanPhaseSpecifier planPhase,
                                              final DateTime requestedDate,
-                                             final DateTime subscriptionStartDate) throws CatalogApiException {
-        final StaticCatalog staticCatalog = getStaticCatalog(planPhase, requestedDate, subscriptionStartDate);
+                                             final DateTime subscriptionChangePlanDate) throws CatalogApiException {
+        final StaticCatalog staticCatalog = getStaticCatalog(planPhase, requestedDate, subscriptionChangePlanDate);
         return staticCatalog.billingAlignment(planPhase);
     }
 
@@ -316,7 +316,7 @@ public class DefaultVersionedCatalog extends ValidatingConfig<DefaultVersionedCa
     public PlanChangeResult planChange(final PlanPhaseSpecifier from,
                                        final PlanSpecifier to,
                                        final DateTime requestedDate,
-                                       final DateTime subscriptionStartDate)
+                                       final DateTime subscriptionChangePlanDate)
             throws CatalogApiException {
         // Use the "to" specifier, to make sure the new plan always exists
         final StaticCatalog staticCatalog = versionForDate(requestedDate);
@@ -324,8 +324,8 @@ public class DefaultVersionedCatalog extends ValidatingConfig<DefaultVersionedCa
     }
 
     // Note that the PlanSpecifier billing period must refer here to the recurring phase one when a plan name isn't specified
-    private StaticCatalog getStaticCatalog(final PlanSpecifier spec, final DateTime requestedDate, final DateTime subscriptionStartDate) throws CatalogApiException {
-        final CatalogPlanEntry entry = findCatalogPlanEntry(new PlanRequestWrapper(spec), requestedDate, subscriptionStartDate);
+    private StaticCatalog getStaticCatalog(final PlanSpecifier spec, final DateTime requestedDate, final DateTime subscriptionChangePlanDate) throws CatalogApiException {
+        final CatalogPlanEntry entry = findCatalogPlanEntry(new PlanRequestWrapper(spec), requestedDate, subscriptionChangePlanDate);
         return entry.getStaticCatalog();
     }
 
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalog.java b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalog.java
index dbdfce6..72b3c67 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalog.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalog.java
@@ -118,14 +118,14 @@ public class MockCatalog extends StandaloneCatalog implements Catalog {
     }
 
     @Override
-    public Plan findPlan(final String name, final DateTime effectiveDate, final DateTime subscriptionStartDate)
+    public Plan findPlan(final String name, final DateTime effectiveDate, final DateTime subscriptionChangePlanDate)
             throws CatalogApiException {
         return findCurrentPlan(name);
     }
 
     @Override
     public Plan createOrFindPlan(final PlanSpecifier spec, final PlanPhasePriceOverridesWithCallContext overrides, final DateTime requestedDate,
-                                 final DateTime subscriptionStartDate) throws CatalogApiException {
+                                 final DateTime subscriptionChangePlanDate) throws CatalogApiException {
         return createOrFindCurrentPlan(spec, overrides);
     }
 
@@ -135,34 +135,34 @@ public class MockCatalog extends StandaloneCatalog implements Catalog {
     }
 
     @Override
-    public PlanPhase findPhase(final String name, final DateTime requestedDate, final DateTime subscriptionStartDate)
+    public PlanPhase findPhase(final String name, final DateTime requestedDate, final DateTime subscriptionChangePlanDate)
             throws CatalogApiException {
         return findCurrentPhase(name);
     }
 
     @Override
-    public PriceList findPriceListForPlan(final String name, final DateTime requestedDate, final DateTime subscriptionStartDate) throws CatalogApiException {
+    public PriceList findPriceListForPlan(final String name, final DateTime requestedDate, final DateTime subscriptionChangePlanDate) throws CatalogApiException {
         return findCurrentPricelist(findCurrentPlan(name).getPriceListName());
     }
 
     @Override
-    public PlanChangeResult planChange(final PlanPhaseSpecifier from, final PlanSpecifier to, final DateTime requestedDate, final DateTime subscriptionStartDate) {
+    public PlanChangeResult planChange(final PlanPhaseSpecifier from, final PlanSpecifier to, final DateTime requestedDate, final DateTime subscriptionChangePlanDate) {
         return planChange(from, to);
     }
 
     @Override
-    public BillingActionPolicy planCancelPolicy(final PlanPhaseSpecifier planPhase, final DateTime requestedDate, final DateTime subscriptionStartDate)
+    public BillingActionPolicy planCancelPolicy(final PlanPhaseSpecifier planPhase, final DateTime requestedDate, final DateTime subscriptionChangePlanDate)
             throws CatalogApiException {
         return planCancelPolicy(planPhase);
     }
 
     @Override
-    public PlanAlignmentCreate planCreateAlignment(final PlanSpecifier specifier, final DateTime requestedDate, final DateTime subscriptionStartDate) {
+    public PlanAlignmentCreate planCreateAlignment(final PlanSpecifier specifier, final DateTime requestedDate, final DateTime subscriptionChangePlanDate) {
         return planCreateAlignment(specifier);
     }
 
     @Override
-    public BillingAlignment billingAlignment(final PlanPhaseSpecifier planPhase, final DateTime requestedDate, final DateTime subscriptionStartDate) {
+    public BillingAlignment billingAlignment(final PlanPhaseSpecifier planPhase, final DateTime requestedDate, final DateTime subscriptionChangePlanDate) {
         return billingAlignment(planPhase);
     }
 
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java b/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
index 4a24cf6..6f1709a 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
@@ -77,14 +77,14 @@ public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
     public void testFindPlanAcrossVersions() throws Exception {
 
         // Existing subscription
-        final DateTime subscriptionStartDate = new DateTime("2011-02-02T00:01:00+00:00"); // dt2 (v2) < subscriptionStartDate < dt2a (v2a)
+        final DateTime subscriptionChangePlanDate = new DateTime("2011-02-02T00:01:00+00:00"); // dt2 (v2) < subscriptionChangePlanDate < dt2a (v2a)
 
 
-        Plan plan = vc.findPlan("shotgun-quarterly", dt2, subscriptionStartDate);
+        Plan plan = vc.findPlan("shotgun-quarterly", dt2, subscriptionChangePlanDate);
         Assert.assertEquals(plan.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("249.95"));
 
         // We still see old price because the requested date is >= dt2a and there is no effectiveDateForExistingSubscriptions
-        plan = vc.findPlan("shotgun-quarterly", dt2a, subscriptionStartDate);
+        plan = vc.findPlan("shotgun-quarterly", dt2a, subscriptionChangePlanDate);
         Assert.assertEquals(plan.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("249.95"));
     }
 
@@ -97,7 +97,7 @@ public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
     @Test(groups = "fast")
     public void testFindPlanAcrossVersionsUsingEffectiveDateForExistingSubscriptions() throws Exception {
 
-        // Easy cases where subscriptionStartDate = requestedDate -> fetch date from the only valid version
+        // Easy cases where subscriptionChangePlanDate = requestedDate -> fetch date from the only valid version
         Plan plan = vc.findPlan("pistol-monthly", dt1, dt1);
         Assert.assertEquals(plan.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("29.95"));
 
@@ -111,17 +111,17 @@ public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
         Assert.assertEquals(plan.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("49.95"));
 
         // Case with an existing subscription (prior first price change)
-        final DateTime subscriptionStartDate = new DateTime("2011-01-01T00:01:00+00:00"); // dt1 (v1) < subscriptionStartDate < dt2 (v2)
+        final DateTime subscriptionChangePlanDate = new DateTime("2011-01-01T00:01:00+00:00"); // dt1 (v1) < subscriptionChangePlanDate < dt2 (v2)
 
         // Returns old price because of effectiveDateForExistingSubscriptions > requestedDate = dt2
-        plan = vc.findPlan("pistol-monthly", dt2, subscriptionStartDate);
+        plan = vc.findPlan("pistol-monthly", dt2, subscriptionChangePlanDate);
         Assert.assertEquals(plan.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("29.95"));
 
         // Returns nw  price because of effectiveDateForExistingSubscriptions = requestedDate = dt2
-        plan = vc.findPlan("pistol-monthly", dEffectiveDateForExistingSubscriptions, subscriptionStartDate);
+        plan = vc.findPlan("pistol-monthly", dEffectiveDateForExistingSubscriptions, subscriptionChangePlanDate);
         Assert.assertEquals(plan.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("39.95"));
 
-        plan = vc.findPlan("pistol-monthly", dt3, subscriptionStartDate);
+        plan = vc.findPlan("pistol-monthly", dt3, subscriptionChangePlanDate);
         Assert.assertEquals(plan.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("39.95"));
     }
 
@@ -133,7 +133,7 @@ public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
 
         final PlanSpecifier pistolMonthly = new PlanSpecifier("Pistol", BillingPeriod.MONTHLY, "DEFAULT");
 
-        // Easy cases where subscriptionStartDate = requestedDate -> fetch date from the only valid version
+        // Easy cases where subscriptionChangePlanDate = requestedDate -> fetch date from the only valid version
         Plan plan = vc.createOrFindPlan(pistolMonthly, null, dt1, dt1);
         Assert.assertEquals(plan.getAllPhases()[1].getRecurring().getRecurringPrice().getPrice(Currency.USD), new BigDecimal("29.95"));
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
index 9756bff..90223d7 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
@@ -139,8 +139,8 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
             final BillingEvent thisEvent = nextEvent;
             nextEvent = eventIt.next();
             if (!events.getSubscriptionIdsWithAutoInvoiceOff().
-                    contains(thisEvent.getSubscription().getId())) { // don't consider events for subscriptions that have auto_invoice_off
-                final BillingEvent adjustedNextEvent = (thisEvent.getSubscription().getId() == nextEvent.getSubscription().getId()) ? nextEvent : null;
+                    contains(thisEvent.getSubscriptionId())) { // don't consider events for subscriptions that have auto_invoice_off
+                final BillingEvent adjustedNextEvent = (thisEvent.getSubscriptionId() == nextEvent.getSubscriptionId()) ? nextEvent : null;
                 final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, thisEvent, adjustedNextEvent, targetDate, currency, invoiceItemGeneratorLogger, thisEvent.getPlan().getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext);
                 proposedItems.addAll(newProposedItems);
             }
@@ -187,7 +187,7 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
         final LocalDate curLocalEffectiveDate = internalCallContext.toLocalDate(currentBillingEvent.getEffectiveDate());
         if (prevComputedFixedItem != null && /* If we have computed a previous item */
             prevComputedFixedItem.getStartDate().compareTo(curLocalEffectiveDate) == 0 && /* The current billing event happens at the same date */
-            prevComputedFixedItem.getSubscriptionId().compareTo(currentBillingEvent.getSubscription().getId()) == 0 /* The current billing event happens for the same subscription */) {
+            prevComputedFixedItem.getSubscriptionId().compareTo(currentBillingEvent.getSubscriptionId()) == 0 /* The current billing event happens for the same subscription */) {
             return true;
         } else {
             return false;
@@ -231,13 +231,14 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
                         if (maxEndDate != null && maxEndDate.compareTo(itemDatum.getEndDate()) < 0) {
                             break;
                         }
+
                         final BigDecimal rate = thisEvent.getRecurringPrice(internalCallContext.toUTCDateTime(itemDatum.getStartDate()));
                         if (rate != null) {
                             final BigDecimal amount = KillBillMoney.of(itemDatum.getNumberOfCycles().multiply(rate), currency);
                             final RecurringInvoiceItem recurringItem = new RecurringInvoiceItem(invoiceId,
                                                                                                 accountId,
-                                                                                                thisEvent.getSubscription().getBundleId(),
-                                                                                                thisEvent.getSubscription().getId(),
+                                                                                                thisEvent.getBundleId(),
+                                                                                                thisEvent.getSubscriptionId(),
                                                                                                 thisEvent.getPlan().getProduct().getName(),
                                                                                                 thisEvent.getPlan().getName(),
                                                                                                 thisEvent.getPlanPhase().getName(),
@@ -247,7 +248,7 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
                             items.add(recurringItem);
                         }
                     }
-                    updatePerSubscriptionNextNotificationDate(thisEvent.getSubscription().getId(), itemDataWithNextBillingCycleDate.getNextBillingCycleDate(), items, billingMode,
+                    updatePerSubscriptionNextNotificationDate(thisEvent.getSubscriptionId(), itemDataWithNextBillingCycleDate.getNextBillingCycleDate(), items, billingMode,
                                                               perSubscriptionFutureNotificationDate);
                 }
             }
@@ -398,8 +399,8 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
             final BigDecimal fixedPrice = thisEvent.getFixedPrice();
 
             if (fixedPrice != null) {
-                final FixedPriceInvoiceItem fixedPriceInvoiceItem = new FixedPriceInvoiceItem(invoiceId, accountId, thisEvent.getSubscription().getBundleId(),
-                                                                                              thisEvent.getSubscription().getId(),
+                final FixedPriceInvoiceItem fixedPriceInvoiceItem = new FixedPriceInvoiceItem(invoiceId, accountId, thisEvent.getBundleId(),
+                                                                                              thisEvent.getSubscriptionId(),
                                                                                               thisEvent.getPlan().getProduct().getName(), thisEvent.getPlan().getName(), thisEvent.getPlanPhase().getName(),
                                                                                               roundedStartDate, fixedPrice, currency);
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java
index a99dc62..3ce549c 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java
@@ -122,7 +122,7 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
                     continue;
                 }
 
-                final UUID subscriptionId = event.getSubscription().getId();
+                final UUID subscriptionId = event.getSubscriptionId();
                 if (curSubscriptionId != null && !curSubscriptionId.equals(subscriptionId)) {
                     final SubscriptionUsageInArrear subscriptionUsageInArrear = new SubscriptionUsageInArrear(account.getId(), invoiceId, curEvents, rawUsgRes.getRawUsage(), rawUsgRes.getExistingTrackingIds(), targetDate, rawUsgRes.getRawUsageStartDate(), usageDetailMode, internalCallContext);
                     final List<InvoiceItem> usageInArrearItems = perSubscriptionInArrearUsageItems.get(curSubscriptionId);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
index 2bc350b..8f516ec 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -426,7 +426,7 @@ public class InvoiceDispatcher {
         final DateTime now = clock.getUTCNow();
         final Map<UUID, DateTime> result = new HashMap<UUID, DateTime>();
         for (final BillingEvent evt : billingEvents) {
-            final UUID subscriptionId = evt.getSubscription().getId();
+            final UUID subscriptionId = evt.getSubscriptionId();
             final DateTime evtEffectiveDate = evt.getEffectiveDate();
             if (evtEffectiveDate.compareTo(now) <= 0) {
                 continue;
@@ -514,12 +514,12 @@ public class InvoiceDispatcher {
         return Iterables.transform(Iterables.filter(billingEvents, new Predicate<BillingEvent>() {
             @Override
             public boolean apply(final BillingEvent input) {
-                return input.getSubscription().getBundleId().equals(dryRunArguments.getBundleId());
+                return input.getBundleId().equals(dryRunArguments.getBundleId());
             }
         }), new Function<BillingEvent, UUID>() {
             @Override
             public UUID apply(final BillingEvent input) {
-                return input.getSubscription().getId();
+                return input.getSubscriptionId();
             }
         });
     }
@@ -675,7 +675,7 @@ public class InvoiceDispatcher {
                                                                                                             new Function<BillingEvent, UUID>() {
                                                                                                                 @Override
                                                                                                                 public UUID apply(final BillingEvent billingEvent) {
-                                                                                                                    return billingEvent.getSubscription().getId();
+                                                                                                                    return billingEvent.getSubscriptionId();
                                                                                                                 }
                                                                                                             }));
         populateNextFutureNotificationDate(rescheduleDate, subscriptionIds, notificationsBuilder, context);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalUsageInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalUsageInArrear.java
index 8bf1ca5..0c39ef4 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalUsageInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalUsageInArrear.java
@@ -471,11 +471,11 @@ public abstract class ContiguousIntervalUsageInArrear {
     }
 
     public UUID getBundleId() {
-        return billingEvents.get(0).getSubscription().getBundleId();
+        return billingEvents.get(0).getBundleId();
     }
 
     public UUID getSubscriptionId() {
-        return billingEvents.get(0).getSubscription().getId();
+        return billingEvents.get(0).getSubscriptionId();
     }
 
     // STEPH_USAGE planName/phaseName,BCD,... might not be correct if we changed plan but Usage section was exactly similar
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionUsageInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionUsageInArrear.java
index a9b11f3..2c58766 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionUsageInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionUsageInArrear.java
@@ -103,7 +103,7 @@ public class SubscriptionUsageInArrear {
         this.rawSubscriptionUsage = Ordering.<RawUsage>from(RAW_USAGE_DATE_COMPARATOR).sortedCopy(Iterables.filter(rawUsage, new Predicate<RawUsage>() {
             @Override
             public boolean apply(final RawUsage input) {
-                return input.getSubscriptionId().equals(subscriptionBillingEvents.get(0).getSubscription().getId());
+                return input.getSubscriptionId().equals(subscriptionBillingEvents.get(0).getSubscriptionId());
             }
         }));
         this.existingTrackingIds = existingTrackingIds;
@@ -132,7 +132,7 @@ public class SubscriptionUsageInArrear {
     }
 
     @VisibleForTesting
-    List<ContiguousIntervalUsageInArrear> computeInArrearUsageInterval() {
+    List<ContiguousIntervalUsageInArrear> computeInArrearUsageInterval() throws CatalogApiException {
         final List<ContiguousIntervalUsageInArrear> usageIntervals = Lists.newLinkedList();
 
         final Map<UsageKey, ContiguousIntervalUsageInArrear> inFlightInArrearUsageIntervals = new HashMap<UsageKey, ContiguousIntervalUsageInArrear>();
@@ -187,7 +187,7 @@ public class SubscriptionUsageInArrear {
         return usageIntervals;
     }
 
-    private List<Usage> findUsageInArrearUsages(final BillingEvent event) {
+    private List<Usage> findUsageInArrearUsages(final BillingEvent event) throws CatalogApiException {
         if (event.getUsages().isEmpty()) {
             return Collections.emptyList();
         }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
index e61343d..eeba00d 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
@@ -358,6 +358,16 @@ public class TestInvoiceHelper {
         Mockito.when(mockAccount.getTimeZone()).thenReturn(DateTimeZone.UTC);
         return new BillingEvent() {
             @Override
+            public UUID getSubscriptionId() {
+                return subscription.getId();
+            }
+
+            @Override
+            public UUID getBundleId() {
+                return subscription.getBundleId();
+            }
+
+            @Override
             public int getBillCycleDayLocal() {
                 return billCycleDayLocal;
             }
@@ -367,10 +377,6 @@ public class TestInvoiceHelper {
                 return null;
             }
 
-            @Override
-            public SubscriptionBase getSubscription() {
-                return subscription;
-            }
 
             @Override
             public DateTime getEffectiveDate() {
@@ -403,7 +409,7 @@ public class TestInvoiceHelper {
             }
 
             @Override
-            public BigDecimal getRecurringPrice(DateTime effectiveDate) {
+            public BigDecimal getRecurringPrice(DateTime requestedDate) {
                 return recurringPrice;
             }
 
@@ -413,6 +419,11 @@ public class TestInvoiceHelper {
             }
 
             @Override
+            public DateTime getLastChangePlanDate() {
+                return effectiveDate;
+            }
+
+            @Override
             public SubscriptionBaseTransitionType getTransitionType() {
                 return type;
             }
@@ -434,8 +445,8 @@ public class TestInvoiceHelper {
 
             @Override
             public int compareTo(final BillingEvent e1) {
-                if (!getSubscription().getId().equals(e1.getSubscription().getId())) { // First order by subscription
-                    return getSubscription().getId().compareTo(e1.getSubscription().getId());
+                if (!getSubscriptionId().equals(e1.getSubscriptionId())) { // First order by subscription
+                    return getSubscriptionId().compareTo(e1.getSubscriptionId());
                 } else { // subscriptions are the same
                     if (!getEffectiveDate().equals(e1.getEffectiveDate())) { // Secondly order by date
                         return getEffectiveDate().compareTo(e1.getEffectiveDate());
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalCapacityInArrear.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalCapacityInArrear.java
index ffc9602..8f63bb2 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalCapacityInArrear.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalCapacityInArrear.java
@@ -62,7 +62,7 @@ import static org.testng.Assert.assertTrue;
 public class TestContiguousIntervalCapacityInArrear extends TestUsageInArrearBase {
 
     @Test(groups = "fast")
-    public void testComputeToBeBilledUsage() {
+    public void testComputeToBeBilledUsage() throws Exception {
 
         final LocalDate startDate = new LocalDate(2014, 03, 20);
         final LocalDate endDate = new LocalDate(2014, 04, 20);
@@ -106,7 +106,7 @@ public class TestContiguousIntervalCapacityInArrear extends TestUsageInArrearBas
     }
 
     @Test(groups = "fast")
-    public void testComputeBilledUsage() throws CatalogApiException {
+    public void testComputeBilledUsage() throws Exception {
 
         final DefaultUnit unit1 = new DefaultUnit().setName("unit1");
         final DefaultUnit unit2 = new DefaultUnit().setName("unit2");
@@ -167,7 +167,7 @@ public class TestContiguousIntervalCapacityInArrear extends TestUsageInArrearBas
 
 
     @Test(groups = "fast")
-    public void testComputeMissingItems() throws CatalogApiException, InvoiceApiException {
+    public void testComputeMissingItems() throws Exception {
 
         final LocalDate startDate = new LocalDate(2014, 03, 20);
         final LocalDate firstBCDDate = new LocalDate(2014, 04, 15);
@@ -246,16 +246,16 @@ public class TestContiguousIntervalCapacityInArrear extends TestUsageInArrearBas
     }
 
     @Test(groups = "fast")
-    public void testMultipleItemsAndTiersAggregateMode() throws CatalogApiException, IOException, InvoiceApiException {
+    public void testMultipleItemsAndTiersAggregateMode() throws Exception {
         testMultipleItemsAndTiers(UsageDetailMode.AGGREGATE);
     }
 
     @Test(groups = "fast")
-    public void testMultipleItemsAndTiersDetailMode() throws CatalogApiException, IOException, InvoiceApiException {
+    public void testMultipleItemsAndTiersDetailMode() throws Exception {
         testMultipleItemsAndTiers(UsageDetailMode.DETAIL);
     }
 
-    private void testMultipleItemsAndTiers(UsageDetailMode usageDetailMode) throws CatalogApiException, IOException, InvoiceApiException {
+    private void testMultipleItemsAndTiers(UsageDetailMode usageDetailMode) throws Exception {
 
         // Case 1
         List<RawUsage> rawUsages = new ArrayList<RawUsage>();
@@ -335,7 +335,7 @@ public class TestContiguousIntervalCapacityInArrear extends TestUsageInArrearBas
     }
 
     @Test(groups = "fast")
-    public void testMultipleItemsAndTiersWithExistingItems() throws CatalogApiException, IOException, InvoiceApiException {
+    public void testMultipleItemsAndTiersWithExistingItems() throws Exception {
 
         // let's assume we have some existing usage
         final UsageInArrearTierUnitDetail existingFooUsageTier1 = new UsageInArrearTierUnitDetail(1, "FOO", BigDecimal.ONE, 9);
@@ -375,7 +375,7 @@ public class TestContiguousIntervalCapacityInArrear extends TestUsageInArrearBas
         assertEquals(itemUnitDetails.get(1).getTierPrice().compareTo(new BigDecimal("100.00")), 0);
     }
 
-    private List<InvoiceItem> produceInvoiceItems(List<RawUsage> rawUsages, UsageDetailMode usageDetailMode, List<InvoiceItem> existingItems) throws CatalogApiException, InvoiceApiException {
+    private List<InvoiceItem> produceInvoiceItems(List<RawUsage> rawUsages, UsageDetailMode usageDetailMode, List<InvoiceItem> existingItems) throws Exception {
 
         final LocalDate startDate = new LocalDate(2014, 03, 20);
         final LocalDate firstBCDDate = new LocalDate(2014, 04, 15);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java
index 6ea9a06..0d38cb3 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java
@@ -77,7 +77,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
                                                                                                });
 
     @Test(groups = "fast")
-    public void testBilledDetailsForUnitType() throws JsonProcessingException {
+    public void testBilledDetailsForUnitType() throws Exception {
 
         final LocalDate startDate = new LocalDate(2014, 03, 20);
         final LocalDate targetDate = startDate.plusDays(1);
@@ -118,7 +118,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
     }
 
     @Test(groups = "fast")
-    public void testComputeToBeBilledUsage() {
+    public void testComputeToBeBilledUsage() throws Exception {
 
         final LocalDate startDate = new LocalDate(2014, 03, 20);
         final LocalDate endDate = new LocalDate(2014, 04, 20);
@@ -157,7 +157,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
     }
 
     @Test(groups = "fast")
-    public void testComputeBilledUsageSizeOneWith_ALL_TIERS() throws CatalogApiException {
+    public void testComputeBilledUsageSizeOneWith_ALL_TIERS() throws Exception {
 
         final DefaultTieredBlock block1 = createDefaultTieredBlock("unit", 1, 10, new BigDecimal("1.5"));
         final DefaultTier tier1 = createDefaultTierWithBlocks(block1);
@@ -186,7 +186,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
     }
 
     @Test(groups = "fast")
-    public void testComputeBilledUsageWith_ALL_TIERS() throws CatalogApiException {
+    public void testComputeBilledUsageWith_ALL_TIERS() throws Exception {
 
         final DefaultTieredBlock block1 = createDefaultTieredBlock("unit", 100, 10, BigDecimal.ONE);
         final DefaultTier tier1 = createDefaultTierWithBlocks(block1);
@@ -213,7 +213,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
 
 
     @Test(groups = "fast")
-    public void testComputeBilledUsageWithUnlimitedMaxWith_ALL_TIERS() throws CatalogApiException {
+    public void testComputeBilledUsageWithUnlimitedMaxWith_ALL_TIERS() throws Exception {
 
         final DefaultTieredBlock block1 = createDefaultTieredBlock("unit", 100, 10, BigDecimal.ONE);
         final DefaultTier tier1 = createDefaultTierWithBlocks(block1);
@@ -241,7 +241,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
 
 
     @Test(groups = "fast")
-    public void testComputeBilledUsageWith_TOP_TIER() throws CatalogApiException {
+    public void testComputeBilledUsageWith_TOP_TIER() throws Exception {
 
         final DefaultTieredBlock block1 = createDefaultTieredBlock("unit", 100, 10, BigDecimal.ONE);
         final DefaultTier tier1 = createDefaultTierWithBlocks(block1);
@@ -288,7 +288,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
 
 
     @Test(groups = "fast")
-    public void testComputeBilledUsageWithUnlimitedMaxWith_TOP_TIER() throws CatalogApiException {
+    public void testComputeBilledUsageWithUnlimitedMaxWith_TOP_TIER() throws Exception {
 
         final DefaultTieredBlock block1 = createDefaultTieredBlock("unit", 100, 10, BigDecimal.TEN);
         final DefaultTier tier1 = createDefaultTierWithBlocks(block1);
@@ -318,7 +318,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
 
 
     @Test(groups = "fast")
-    public void testComputeBilledUsageSizeOneWith_TOP_TIER() throws CatalogApiException {
+    public void testComputeBilledUsageSizeOneWith_TOP_TIER() throws Exception {
 
         final DefaultTieredBlock block1 = createDefaultTieredBlock("unit", 1, 10, new BigDecimal("1.5"));
         final DefaultTier tier1 = createDefaultTierWithBlocks(block1);
@@ -346,7 +346,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
     }
 
     @Test(groups = "fast")
-    public void testComputeMissingItems() throws CatalogApiException, IOException, InvoiceApiException {
+    public void testComputeMissingItems() throws Exception {
 
         final LocalDate startDate = new LocalDate(2014, 03, 20);
         final LocalDate firstBCDDate = new LocalDate(2014, 04, 15);
@@ -425,7 +425,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
     }
 
     @Test(groups = "fast")
-    public void testGetRolledUpUsage() {
+    public void testGetRolledUpUsage() throws Exception {
 
         final DefaultTieredBlock tieredBlock1 = createDefaultTieredBlock("unit", 100, 1000, BigDecimal.ONE);
         final DefaultTieredBlock tieredBlock2 = createDefaultTieredBlock("unit2", 10, 1000, BigDecimal.ONE);
@@ -500,7 +500,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
     }
 
     @Test(groups = "fast", description = "See https://github.com/killbill/killbill/issues/706")
-    public void testWithRawUsageStartDateAfterEndDate() throws CatalogApiException {
+    public void testWithRawUsageStartDateAfterEndDate() throws Exception {
 
         final LocalDate startDate = new LocalDate(2014, 10, 16);
         final LocalDate endDate = startDate;
@@ -530,7 +530,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
     }
 
     @Test(groups = "fast")
-    public void testBilledUsage() throws CatalogApiException {
+    public void testBilledUsage() throws Exception {
 
         final DefaultTieredBlock block1 = createDefaultTieredBlock("cell-phone-minutes", 1000, 10000, new BigDecimal("0.5"));
         final DefaultTieredBlock block2 = createDefaultTieredBlock("Mbytes", 512, 512000, new BigDecimal("0.3"));
@@ -554,7 +554,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
     }
 
     @Test(groups = "fast")
-    public void testComputeMissingItemsAggregateModeAllTier_AGGREGATE() throws CatalogApiException, IOException, InvoiceApiException {
+    public void testComputeMissingItemsAggregateModeAllTier_AGGREGATE() throws Exception {
 
         // Case 1
         List<RawUsage> rawUsages = new ArrayList<RawUsage>();
@@ -656,7 +656,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
     }
 
     @Test(groups = "fast")
-    public void testComputeMissingItemsDetailModeAllTier_DETAIL() throws CatalogApiException, IOException, InvoiceApiException {
+    public void testComputeMissingItemsDetailModeAllTier_DETAIL() throws Exception {
 
         // Case 1
         List<RawUsage> rawUsages = new ArrayList<RawUsage>();
@@ -724,7 +724,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
     }
 
     @Test(groups = "fast")
-    public void testComputeMissingItemsAggregateModeTopTier_AGGREGATE() throws CatalogApiException, IOException, InvoiceApiException {
+    public void testComputeMissingItemsAggregateModeTopTier_AGGREGATE() throws Exception {
 
         // Case 1
         List<RawUsage> rawUsages = new ArrayList<RawUsage>();
@@ -801,7 +801,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
     }
 
     @Test(groups = "fast")
-    public void testComputeMissingItemsDetailModeTopTier_DETAIL() throws CatalogApiException, IOException, InvoiceApiException {
+    public void testComputeMissingItemsDetailModeTopTier_DETAIL() throws Exception {
 
         // Case 1
         List<RawUsage> rawUsages = new ArrayList<RawUsage>();
@@ -853,7 +853,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
     }
 
     @Test(groups = "fast")
-    public void testMultipleItemsAndTiersWithExistingItemsAllTiers_AGGREGATE() throws CatalogApiException, IOException, InvoiceApiException {
+    public void testMultipleItemsAndTiersWithExistingItemsAllTiers_AGGREGATE() throws Exception {
 
         //
         // Let's assume we were already billed on the previous period
@@ -929,7 +929,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
     }
 
     @Test(groups = "fast")
-    public void testMultipleItemsAndTiersWithExistingItemsAllTiers_DETAIL() throws CatalogApiException, IOException, InvoiceApiException {
+    public void testMultipleItemsAndTiersWithExistingItemsAllTiers_DETAIL() throws Exception {
 
         //
         // Create usage data points (will include already billed + add new usage data)
@@ -1001,12 +1001,12 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
     }
 
     @Test(groups = "fast")
-    public void testMultipleItemsAndTiersWithExistingItemsTopTier() throws CatalogApiException, IOException {
+    public void testMultipleItemsAndTiersWithExistingItemsTopTier() throws Exception {
         // TODO + code
     }
 
     @Test(groups = "fast")
-    public void testGetRolledUpUsageOnlyUsageBeforeTransitionTime() {
+    public void testGetRolledUpUsageOnlyUsageBeforeTransitionTime() throws Exception {
 
         final DefaultTieredBlock tieredBlock1 = createDefaultTieredBlock("unit", 100, 1000, BigDecimal.ONE);
         final DefaultTieredBlock tieredBlock2 = createDefaultTieredBlock("unit2", 10, 1000, BigDecimal.ONE);
@@ -1041,7 +1041,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
 
     }
 
-    private List<InvoiceItem> produceInvoiceItems(List<RawUsage> rawUsages, TierBlockPolicy tierBlockPolicy, UsageDetailMode usageDetailMode, final List<InvoiceItem> existingItems) throws CatalogApiException, InvoiceApiException {
+    private List<InvoiceItem> produceInvoiceItems(List<RawUsage> rawUsages, TierBlockPolicy tierBlockPolicy, UsageDetailMode usageDetailMode, final List<InvoiceItem> existingItems) throws Exception {
 
         final LocalDate startDate = new LocalDate(2014, 03, 20);
         final LocalDate firstBCDDate = new LocalDate(2014, 04, 15);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalUsageInArrear.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalUsageInArrear.java
index 326e3c6..cc7a9e5 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalUsageInArrear.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalUsageInArrear.java
@@ -38,7 +38,7 @@ import com.google.common.collect.ImmutableList;
 public class TestContiguousIntervalUsageInArrear extends TestUsageInArrearBase {
 
     @Test(groups = "fast")
-    public void testVerifyTransitionTimes() {
+    public void testVerifyTransitionTimes() throws Exception {
         final DefaultTieredBlock block = createDefaultTieredBlock("unit", 100, 1000, BigDecimal.ONE);
         final DefaultTier tier = createDefaultTierWithBlocks(block);
         final DefaultUsage usage = createConsumableInArrearUsage(usageName, BillingPeriod.MONTHLY, TierBlockPolicy.ALL_TIERS, tier);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java
index c9925af..61729eb 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java
@@ -43,7 +43,7 @@ import static org.testng.Assert.assertTrue;
 public class TestSubscriptionConsumableInArrear extends TestUsageInArrearBase {
 
     @Test(groups = "fast")
-    public void testComputeInArrearUsageInterval() {
+    public void testComputeInArrearUsageInterval() throws Exception {
 
         final List<BillingEvent> billingEvents = Lists.newArrayList();
 
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java
index 5daaec1..860a938 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java
@@ -37,6 +37,7 @@ import org.killbill.billing.catalog.DefaultUnit;
 import org.killbill.billing.catalog.DefaultUsage;
 import org.killbill.billing.catalog.api.BillingMode;
 import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
@@ -177,16 +178,18 @@ public abstract class TestUsageInArrearBase extends InvoiceTestSuiteNoDB {
         return block;
     }
 
-    protected BillingEvent createMockBillingEvent(final DateTime effectiveDate, final BillingPeriod billingPeriod, final List<Usage> usages) {
+    protected BillingEvent createMockBillingEvent(final DateTime effectiveDate, final BillingPeriod billingPeriod, final List<Usage> usages) throws Exception {
         return createMockBillingEvent(BCD, effectiveDate, billingPeriod, usages);
     }
 
-    protected BillingEvent createMockBillingEvent(final int bcd, final DateTime effectiveDate, final BillingPeriod billingPeriod, final List<Usage> usages) {
+    protected BillingEvent createMockBillingEvent(final int bcd, final DateTime effectiveDate, final BillingPeriod billingPeriod, final List<Usage> usages) throws Exception {
         final BillingEvent result = Mockito.mock(BillingEvent.class);
         Mockito.when(result.getCurrency()).thenReturn(Currency.BTC);
         Mockito.when(result.getBillCycleDayLocal()).thenReturn(bcd);
         Mockito.when(result.getEffectiveDate()).thenReturn(effectiveDate);
         Mockito.when(result.getBillingPeriod()).thenReturn(billingPeriod);
+        Mockito.when(result.getSubscriptionId()).thenReturn(subscriptionId);
+        Mockito.when(result.getBundleId()).thenReturn(bundleId);
 
         final Account account = Mockito.mock(Account.class);
         Mockito.when(account.getId()).thenReturn(accountId);
@@ -194,7 +197,6 @@ public abstract class TestUsageInArrearBase extends InvoiceTestSuiteNoDB {
         final SubscriptionBase subscription = Mockito.mock(SubscriptionBase.class);
         Mockito.when(subscription.getId()).thenReturn(subscriptionId);
         Mockito.when(subscription.getBundleId()).thenReturn(bundleId);
-        Mockito.when(result.getSubscription()).thenReturn(subscription);
 
         final Product product = Mockito.mock(Product.class);
         Mockito.when(product.getName()).thenReturn(productName);
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
index 0da70fa..7ab83c6 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
@@ -74,13 +74,11 @@ public class BlockingCalculator {
      *
      * @param billingEvents the original list of billing events to update (without overdue events)
      */
-    public boolean insertBlockingEvents(final SortedSet<BillingEvent> billingEvents, final Set<UUID> skippedSubscriptions, final Catalog catalog, final InternalTenantContext context) throws CatalogApiException {
+    public boolean insertBlockingEvents(final SortedSet<BillingEvent> billingEvents, final Set<UUID> skippedSubscriptions, final Map<UUID, List<SubscriptionBase>> subscriptionsForAccount, final Catalog catalog, final InternalTenantContext context) throws CatalogApiException {
         if (billingEvents.size() <= 0) {
             return false;
         }
 
-        final Hashtable<UUID, List<SubscriptionBase>> bundleMap = createBundleSubscriptionMap(billingEvents);
-
         final SortedSet<BillingEvent> billingEventsToAdd = new TreeSet<BillingEvent>();
         final SortedSet<BillingEvent> billingEventsToRemove = new TreeSet<BillingEvent>();
 
@@ -96,11 +94,11 @@ public class BlockingCalculator {
         final Map<UUID, List<BlockingState>> perBundleBlockingEvents = getPerTypeBlockingEvents(BlockingStateType.SUBSCRIPTION_BUNDLE, blockingEvents);
         final Map<UUID, List<BlockingState>> perSubscriptionBlockingEvents = getPerTypeBlockingEvents(BlockingStateType.SUBSCRIPTION, blockingEvents);
 
-        for (final UUID bundleId : bundleMap.keySet()) {
+        for (final UUID bundleId : subscriptionsForAccount.keySet()) {
 
             final List<BlockingState> bundleBlockingEvents = perBundleBlockingEvents.get(bundleId) != null ? perBundleBlockingEvents.get(bundleId) : ImmutableList.<BlockingState>of();
 
-            for (final SubscriptionBase subscription : bundleMap.get(bundleId)) {
+            for (final SubscriptionBase subscription : subscriptionsForAccount.get(bundleId)) {
                 // Avoid inserting additional events for subscriptions that don't even have a START event
                 if (skippedSubscriptions.contains(subscription.getId())) {
                     continue;
@@ -228,7 +226,7 @@ public class BlockingCalculator {
     protected SortedSet<BillingEvent> filter(final SortedSet<BillingEvent> billingEvents, final SubscriptionBase subscription) {
         final SortedSet<BillingEvent> result = new TreeSet<BillingEvent>();
         for (final BillingEvent event : billingEvents) {
-            if (event.getSubscription().getId().equals(subscription.getId())) {
+            if (event.getSubscriptionId().equals(subscription.getId())) {
                 result.add(event);
             }
         }
@@ -238,7 +236,6 @@ public class BlockingCalculator {
     protected BillingEvent createNewDisableEvent(final DateTime disabledDurationStart, final BillingEvent previousEvent, final Catalog catalog) throws CatalogApiException {
 
         final int billCycleDay = previousEvent.getBillCycleDayLocal();
-        final SubscriptionBase subscription = previousEvent.getSubscription();
         final DateTime effectiveDate = disabledDurationStart;
         final PlanPhase planPhase = previousEvent.getPlanPhase();
         final Plan plan = previousEvent.getPlan();
@@ -246,6 +243,7 @@ public class BlockingCalculator {
         // Make sure to set the fixed price to null and the billing period to NO_BILLING_PERIOD,
         // which makes invoice disregard this event
         final BigDecimal fixedPrice = null;
+        final BigDecimal recurringPrice = null;
         final BillingPeriod billingPeriod = BillingPeriod.NO_BILLING_PERIOD;
 
         final Currency currency = previousEvent.getCurrency();
@@ -253,16 +251,26 @@ public class BlockingCalculator {
         final SubscriptionBaseTransitionType type = SubscriptionBaseTransitionType.START_BILLING_DISABLED;
         final Long totalOrdering = globaltotalOrder.getAndIncrement();
 
-        return new DefaultBillingEvent(subscription, effectiveDate, true, plan, planPhase, fixedPrice,
+        return new DefaultBillingEvent(previousEvent.getSubscriptionId(),
+                                       previousEvent.getBundleId(),
+                                       effectiveDate,
+                                       plan,
+                                       planPhase,
+                                       fixedPrice,
                                        currency,
-                                       billingPeriod, billCycleDay,
-                                       description, totalOrdering, type, catalog, true);
+                                       billingPeriod,
+                                       previousEvent.getLastChangePlanDate(),
+                                       billCycleDay,
+                                       description,
+                                       totalOrdering,
+                                       type,
+                                       true,
+                                       catalog);
     }
 
     protected BillingEvent createNewReenableEvent(final DateTime odEventTime, final BillingEvent previousEvent, final Catalog catalog, final InternalTenantContext context) throws CatalogApiException {
         // All fields are populated with the event state from before the blocking period, for invoice to resume invoicing
         final int billCycleDay = previousEvent.getBillCycleDayLocal();
-        final SubscriptionBase subscription = previousEvent.getSubscription();
         final DateTime effectiveDate = odEventTime;
         final PlanPhase planPhase = previousEvent.getPlanPhase();
         final BigDecimal fixedPrice = previousEvent.getFixedPrice();
@@ -273,26 +281,21 @@ public class BlockingCalculator {
         final SubscriptionBaseTransitionType type = SubscriptionBaseTransitionType.END_BILLING_DISABLED;
         final Long totalOrdering = globaltotalOrder.getAndIncrement();
 
-        return new DefaultBillingEvent(subscription, effectiveDate, true, plan, planPhase, fixedPrice,
+        return new DefaultBillingEvent(previousEvent.getSubscriptionId(),
+                                       previousEvent.getBundleId(),
+                                       effectiveDate,
+                                       plan,
+                                       planPhase,
+                                       fixedPrice,
                                        currency,
-                                       billingPeriod, billCycleDay,
-                                       description, totalOrdering, type, catalog, false);
-    }
-
-    protected Hashtable<UUID, List<SubscriptionBase>> createBundleSubscriptionMap(final SortedSet<BillingEvent> billingEvents) {
-        final Hashtable<UUID, List<SubscriptionBase>> result = new Hashtable<UUID, List<SubscriptionBase>>();
-        for (final BillingEvent event : billingEvents) {
-            final UUID bundleId = event.getSubscription().getBundleId();
-            List<SubscriptionBase> subs = result.get(bundleId);
-            if (subs == null) {
-                subs = new ArrayList<SubscriptionBase>();
-                result.put(bundleId, subs);
-            }
-            if (!result.get(bundleId).contains(event.getSubscription())) {
-                subs.add(event.getSubscription());
-            }
-        }
-        return result;
+                                       billingPeriod,
+                                       previousEvent.getLastChangePlanDate(),
+                                       billCycleDay,
+                                       description,
+                                       totalOrdering,
+                                       type,
+                                       false,
+                                       catalog);
     }
 
     // In ascending order
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEvent.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEvent.java
index 43da1ab..9e7dfe4 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEvent.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEvent.java
@@ -20,8 +20,7 @@ package org.killbill.billing.junction.plumbing.billing;
 
 import java.math.BigDecimal;
 import java.util.List;
-
-import javax.annotation.Nullable;
+import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.killbill.billing.catalog.api.BillingAlignment;
@@ -32,153 +31,120 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.Usage;
-import org.killbill.billing.events.SubscriptionInternalEvent;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
+import org.killbill.billing.subscription.api.user.SubscriptionBillingEvent;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
 public class DefaultBillingEvent implements BillingEvent {
 
+    private final UUID subscriptionId;
+    private final UUID bundleId;
+
     private final int billCycleDayLocal;
     private final BillingAlignment billingAlignment;
-    private final SubscriptionBase subscription;
+
     private final DateTime effectiveDate;
+
     private final PlanPhase planPhase;
     private final Plan plan;
+    private final BillingPeriod billingPeriod;
+
     private final BigDecimal fixedPrice;
     private final Currency currency;
     private final String description;
-    private final BillingPeriod billingPeriod;
     private final SubscriptionBaseTransitionType type;
     private final Long totalOrdering;
 
-    private final List<Usage> usages;
-
-    private final Catalog catalog;
-    private final boolean isDisableEvent;
-    private final PlanPhase nextPlanPhase;
+    private final boolean isCancelledOrBlocked;
 
     private final DateTime catalogEffectiveDate;
+    private final Catalog catalog;
+    private final DateTime lastChangePlanDate;
 
-    public DefaultBillingEvent(final SubscriptionInternalEvent transition,
+    public DefaultBillingEvent(final SubscriptionBillingEvent inputEvent,
                                final SubscriptionBase subscription,
                                final int billCycleDayLocal,
                                final BillingAlignment billingAlignment,
                                final Currency currency,
                                final Catalog catalog) throws CatalogApiException {
-        final boolean isActive = transition.getTransitionType() != SubscriptionBaseTransitionType.CANCEL;
+        this.subscriptionId = subscription.getId();
+        this.bundleId = subscription.getBundleId();
 
-        if (isActive) {
-            final String planName = transition.getNextPlan();
-            this.plan = (planName != null) ? catalog.findPlan(planName, transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
+        this.isCancelledOrBlocked = inputEvent.getType() == SubscriptionBaseTransitionType.CANCEL;
 
-            final String planPhaseName = transition.getNextPhase();
-            this.planPhase = (planPhaseName != null && this.plan != null) ? this.plan.findPhase(planPhaseName) : null;
-            this.nextPlanPhase = this.planPhase;
+        this.lastChangePlanDate = inputEvent.getLastChangePlanDate();
+        this.type = inputEvent.getType();
+        this.plan = catalog.findPlan(inputEvent.getPlanName(), inputEvent.getEffectiveDate(), lastChangePlanDate);
+        this.planPhase = this.plan.findPhase(inputEvent.getPlanPhaseName());
 
-            this.billingPeriod = getRecurringBillingPeriod(nextPlanPhase);
-        } else {
-            final String planName = transition.getPreviousPlan();
-            this.plan = (planName != null) ? catalog.findPlan(planName, transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
-            final Plan prevPlan = this.plan;
-
-            final String planPhaseName = transition.getPreviousPhase();
-            this.planPhase = (planPhaseName != null && this.plan != null) ? this.plan.findPhase(planPhaseName) : null;
-            this.nextPlanPhase = null;
-
-            final String prevPhaseName = transition.getPreviousPhase();
-            final PlanPhase prevPlanPhase = (prevPhaseName != null && prevPlan != null) ? prevPlan.findPhase(prevPhaseName) : null;
-            this.billingPeriod = getRecurringBillingPeriod(prevPlanPhase);
-        }
-        this.catalogEffectiveDate = plan == null ? null : new DateTime(plan.getCatalog().getEffectiveDate());
+        this.catalog = catalog;
+        this.catalogEffectiveDate = new DateTime(plan.getCatalog().getEffectiveDate());
 
+        this.currency = currency;
         this.billCycleDayLocal = billCycleDayLocal;
         this.billingAlignment = billingAlignment;
-        this.catalog = catalog;
-        this.currency = currency;
-        this.description = transition.getTransitionType().toString();
-        this.effectiveDate = transition.getEffectiveTransitionTime();
-        this.fixedPrice = transition.getTransitionType() != SubscriptionBaseTransitionType.BCD_CHANGE ? getFixedPrice(nextPlanPhase, currency) : null;
-        this.isDisableEvent = false;
-        this.subscription = subscription;
-        this.totalOrdering = transition.getTotalOrdering();
-        this.type = transition.getTransitionType();
-        this.usages = initializeUsage(isActive);
+        this.description = inputEvent.getType().toString();
+        this.effectiveDate = inputEvent.getEffectiveDate();
+        this.totalOrdering = inputEvent.getTotalOrdering();
+
+        // Those 2, billingPeriod and fixedPrice can be computed once and for all as they don't move for all invoicing period
+        // related to this billing event.
+        this.billingPeriod = computeRecurringBillingPeriod(planPhase);
+        this.fixedPrice = computeFixedPrice(isCancelledOrBlocked, planPhase, currency, type);
     }
 
-    public DefaultBillingEvent(final SubscriptionBase subscription, final DateTime effectiveDate, final boolean isActive,
-                               final Plan plan, final PlanPhase planPhase, final BigDecimal fixedPrice,
+    public DefaultBillingEvent(final UUID subscriptionId,
+                               final UUID bundleId,
+                               final DateTime effectiveDate,
+                               final Plan plan,
+                               final PlanPhase planPhase,
+                               final BigDecimal fixedPrice,
                                final Currency currency,
-                               final BillingPeriod billingPeriod, final int billCycleDayLocal,
-                               final String description, final long totalOrdering, final SubscriptionBaseTransitionType type,
-                               final Catalog catalog,
-                               final boolean isDisableEvent) throws CatalogApiException {
+                               final BillingPeriod billingPeriod,
+                               final DateTime lastChangePlanDate,
+                               final int billCycleDayLocal,
+                               final String description,
+                               final long totalOrdering,
+                               final SubscriptionBaseTransitionType type,
+                               final boolean isDisableEvent,
+                               final Catalog catalog) throws CatalogApiException {
+        this.subscriptionId = subscriptionId;
+        this.bundleId = bundleId;
+
         this.catalog = catalog;
-        this.subscription = subscription;
+        this.catalogEffectiveDate = new DateTime(plan.getCatalog().getEffectiveDate());
+
         this.effectiveDate = effectiveDate;
+        this.lastChangePlanDate = lastChangePlanDate;
+
+        this.isCancelledOrBlocked = isDisableEvent;
+
         this.plan = plan;
         this.planPhase = planPhase;
+        this.billingPeriod = billingPeriod;
         this.fixedPrice = fixedPrice;
         this.currency = currency;
-        this.billingPeriod = billingPeriod;
         this.billCycleDayLocal = billCycleDayLocal;
         this.description = description;
         this.type = type;
         this.totalOrdering = totalOrdering;
-        this.usages = initializeUsage(isActive);
-        this.isDisableEvent = isDisableEvent;
-        this.nextPlanPhase = isDisableEvent ? null : planPhase;
-        this.catalogEffectiveDate = plan != null ? new DateTime(plan.getCatalog().getEffectiveDate()) : null;
         this.billingAlignment = null;
+
     }
 
+
     @Override
-    public int compareTo(final BillingEvent e1) {
-        if (!getSubscription().getId().equals(e1.getSubscription().getId())) { // First order by subscription
-            return getSubscription().getId().compareTo(e1.getSubscription().getId());
-        } else { // subscriptions are the same
-            if (!getEffectiveDate().equals(e1.getEffectiveDate())) { // Secondly order by date
-                return getEffectiveDate().compareTo(e1.getEffectiveDate());
-            } else { // dates and subscriptions are the same
-                // If an subscription event and an overdue event happen at the exact same time,
-                // we assume we want the subscription event before the overdue event when entering
-                // the overdue period, and vice-versa when exiting the overdue period
-                if (SubscriptionBaseTransitionType.START_BILLING_DISABLED.equals(getTransitionType())) {
-                    if (SubscriptionBaseTransitionType.END_BILLING_DISABLED.equals(e1.getTransitionType())) {
-                        // Make sure to always have START before END
-                        return -1;
-                    } else {
-                        return 1;
-                    }
-                } else if (SubscriptionBaseTransitionType.START_BILLING_DISABLED.equals(e1.getTransitionType())) {
-                    if (SubscriptionBaseTransitionType.END_BILLING_DISABLED.equals(getTransitionType())) {
-                        // Make sure to always have START before END
-                        return 1;
-                    } else {
-                        return -1;
-                    }
-                } else if (SubscriptionBaseTransitionType.END_BILLING_DISABLED.equals(getTransitionType())) {
-                    if (SubscriptionBaseTransitionType.START_BILLING_DISABLED.equals(e1.getTransitionType())) {
-                        // Make sure to always have START before END
-                        return 1;
-                    } else {
-                        return -1;
-                    }
-                } else if (SubscriptionBaseTransitionType.END_BILLING_DISABLED.equals(e1.getTransitionType())) {
-                    if (SubscriptionBaseTransitionType.START_BILLING_DISABLED.equals(getTransitionType())) {
-                        // Make sure to always have START before END
-                        return -1;
-                    } else {
-                        return 1;
-                    }
-                } else {
-                    return getTotalOrdering().compareTo(e1.getTotalOrdering());
-                }
-            }
-        }
+    public UUID getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    @Override
+    public UUID getBundleId() {
+        return bundleId;
     }
 
     @Override
@@ -192,11 +158,6 @@ public class DefaultBillingEvent implements BillingEvent {
     }
 
     @Override
-    public SubscriptionBase getSubscription() {
-        return subscription;
-    }
-
-    @Override
     public DateTime getEffectiveDate() {
         return effectiveDate;
     }
@@ -221,18 +182,10 @@ public class DefaultBillingEvent implements BillingEvent {
         return description;
     }
 
-    @Override
-    public BigDecimal getFixedPrice() {
-        return fixedPrice;
-    }
 
     @Override
-    public BigDecimal getRecurringPrice(final DateTime effectiveDate) throws CatalogApiException {
-        if (isDisableEvent || nextPlanPhase == null) {
-            return null;
-        }
-        final PlanPhase effectivePlanPhase = effectiveDate != null ? catalog.findPhase(nextPlanPhase.getName(), effectiveDate, subscription.getStartDate()) : nextPlanPhase;
-        return getRecurringPrice(effectivePlanPhase, currency);
+    public DateTime getLastChangePlanDate() {
+        return lastChangePlanDate;
     }
 
     @Override
@@ -251,23 +204,109 @@ public class DefaultBillingEvent implements BillingEvent {
     }
 
     @Override
-    public List<Usage> getUsages() {
-        return usages;
+    public BigDecimal getFixedPrice() {
+        return fixedPrice;
     }
 
+
     @Override
-    public String toString() {
-        // Note: we don't use all fields here, as the output would be overwhelming
-        // (these events are printed in the logs in junction and invoice).
-        final StringBuilder sb = new StringBuilder();
-        sb.append("DefaultBillingEvent");
-        sb.append("{type=").append(type);
-        sb.append(", effectiveDate=").append(effectiveDate);
-        sb.append(", planPhaseName=").append(planPhase.getName());
-        sb.append(", subscriptionId=").append(subscription.getId());
-        sb.append(", totalOrdering=").append(totalOrdering);
-        sb.append('}');
-        return sb.toString();
+    public BigDecimal getRecurringPrice(final DateTime requestedDate) throws CatalogApiException {
+        final PlanPhase effectivePlanPhase = catalog.findPhase(planPhase.getName(), requestedDate, lastChangePlanDate);
+        return computeRecurringPrice(isCancelledOrBlocked, effectivePlanPhase, currency);
+    }
+
+    @Override
+    public List<Usage> getUsages(/*DateTime requestedDate*/) throws CatalogApiException {
+        final PlanPhase effectivePlanPhase = catalog.findPhase(planPhase.getName(), effectiveDate, lastChangePlanDate);
+        return computeUsages(isCancelledOrBlocked, effectivePlanPhase);
+    }
+
+
+
+    private static BigDecimal computeFixedPrice(final boolean isCancelledOrBlocked, final PlanPhase effectivePlanPhase, final Currency currency, final SubscriptionBaseTransitionType type) throws CatalogApiException {
+        if (isCancelledOrBlocked ||
+            type == SubscriptionBaseTransitionType.BCD_CHANGE /* We don't want to bill twice for the same fixed price */) {
+            return null;
+        }
+        return (effectivePlanPhase.getFixed() != null && effectivePlanPhase.getFixed().getPrice() != null) ? effectivePlanPhase.getFixed().getPrice().getPrice(currency) : null;
+    }
+
+    private static BigDecimal computeRecurringPrice(final boolean isCancelledOrBlocked, final PlanPhase effectivePlanPhase, final Currency currency) throws CatalogApiException {
+        if (isCancelledOrBlocked) {
+            return null;
+        }
+        return (effectivePlanPhase.getRecurring() != null && effectivePlanPhase.getRecurring().getRecurringPrice() != null) ? effectivePlanPhase.getRecurring().getRecurringPrice().getPrice(currency) : null;
+    }
+
+    private static BillingPeriod computeRecurringBillingPeriod(final PlanPhase effectivePlanPhase) {
+        return effectivePlanPhase.getRecurring() != null ? effectivePlanPhase.getRecurring().getBillingPeriod() : BillingPeriod.NO_BILLING_PERIOD;
+    }
+
+    private static List<Usage> computeUsages(final boolean isCancelledOrBlocked, final PlanPhase effectivePlanPhase) {
+        if (isCancelledOrBlocked) {
+            return ImmutableList.<Usage>of();
+        }
+
+        final List<Usage> result = (effectivePlanPhase.getUsages().length > 0) ?
+                             Lists.newArrayList() : ImmutableList.<Usage>of();
+        for (Usage usage : effectivePlanPhase.getUsages()) {
+            result.add(usage);
+        }
+        return result;
+    }
+
+
+
+    @Override
+    public DateTime getCatalogEffectiveDate() {
+        // TODO Can that go ?
+        return catalogEffectiveDate;
+    }
+
+    @Override
+    public int compareTo(final BillingEvent e1) {
+        if (!subscriptionId.equals(e1.getSubscriptionId())) { // First order by subscription
+            return subscriptionId.compareTo(e1.getSubscriptionId());
+        } else { // subscriptions are the same
+            if (!getEffectiveDate().equals(e1.getEffectiveDate())) { // Secondly order by date
+                return getEffectiveDate().compareTo(e1.getEffectiveDate());
+            } else { // dates and subscriptions are the same
+                // If an subscription event and an overdue event happen at the exact same time,
+                // we assume we want the subscription event before the overdue event when entering
+                // the overdue period, and vice-versa when exiting the overdue period
+                if (SubscriptionBaseTransitionType.START_BILLING_DISABLED.equals(getTransitionType())) {
+                    if (SubscriptionBaseTransitionType.END_BILLING_DISABLED.equals(e1.getTransitionType())) {
+                        // Make sure to always have START before END
+                        return -1;
+                    } else {
+                        return 1;
+                    }
+                } else if (SubscriptionBaseTransitionType.START_BILLING_DISABLED.equals(e1.getTransitionType())) {
+                    if (SubscriptionBaseTransitionType.END_BILLING_DISABLED.equals(getTransitionType())) {
+                        // Make sure to always have START before END
+                        return 1;
+                    } else {
+                        return -1;
+                    }
+                } else if (SubscriptionBaseTransitionType.END_BILLING_DISABLED.equals(getTransitionType())) {
+                    if (SubscriptionBaseTransitionType.START_BILLING_DISABLED.equals(e1.getTransitionType())) {
+                        // Make sure to always have START before END
+                        return 1;
+                    } else {
+                        return -1;
+                    }
+                } else if (SubscriptionBaseTransitionType.END_BILLING_DISABLED.equals(e1.getTransitionType())) {
+                    if (SubscriptionBaseTransitionType.START_BILLING_DISABLED.equals(getTransitionType())) {
+                        // Make sure to always have START before END
+                        return -1;
+                    } else {
+                        return 1;
+                    }
+                } else {
+                    return getTotalOrdering().compareTo(e1.getTotalOrdering());
+                }
+            }
+        }
     }
 
     @Override
@@ -281,6 +320,12 @@ public class DefaultBillingEvent implements BillingEvent {
 
         final DefaultBillingEvent that = (DefaultBillingEvent) o;
 
+        if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
+            return false;
+        }
+        if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
+            return false;
+        }
         if (billCycleDayLocal != that.billCycleDayLocal) {
             return false;
         }
@@ -305,9 +350,6 @@ public class DefaultBillingEvent implements BillingEvent {
         if (planPhase != null ? !planPhase.equals(that.planPhase) : that.planPhase != null) {
             return false;
         }
-        if (subscription != null ? !subscription.equals(that.subscription) : that.subscription != null) {
-            return false;
-        }
         if (totalOrdering != null ? !totalOrdering.equals(that.totalOrdering) : that.totalOrdering != null) {
             return false;
         }
@@ -321,7 +363,8 @@ public class DefaultBillingEvent implements BillingEvent {
     @Override
     public int hashCode() {
         int result = 31 * billCycleDayLocal;
-        result = 31 * result + (subscription != null ? subscription.hashCode() : 0);
+        result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
+        result = 31 * result + (bundleId != null ? bundleId.hashCode() : 0);
         result = 31 * result + (fixedPrice != null ? fixedPrice.hashCode() : 0);
         result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
         result = 31 * result + (planPhase != null ? planPhase.hashCode() : 0);
@@ -334,37 +377,20 @@ public class DefaultBillingEvent implements BillingEvent {
         return result;
     }
 
-    private BigDecimal getFixedPrice(@Nullable final PlanPhase nextPhase, final Currency currency) throws CatalogApiException {
-        return (nextPhase != null && nextPhase.getFixed() != null && nextPhase.getFixed().getPrice() != null) ? nextPhase.getFixed().getPrice().getPrice(currency) : null;
-    }
-
-    private BigDecimal getRecurringPrice(@Nullable final PlanPhase nextPhase, final Currency currency) throws CatalogApiException {
-        return (nextPhase != null && nextPhase.getRecurring() != null && nextPhase.getRecurring().getRecurringPrice() != null) ? nextPhase.getRecurring().getRecurringPrice().getPrice(currency) : null;
-    }
-
-    private BillingPeriod getRecurringBillingPeriod(@Nullable final PlanPhase nextPhase) {
-        if (nextPhase == null) {
-            return BillingPeriod.NO_BILLING_PERIOD;
-        }
-        return nextPhase.getRecurring() != null ? nextPhase.getRecurring().getBillingPeriod() : BillingPeriod.NO_BILLING_PERIOD;
+    @Override
+    public String toString() {
+        // Note: we don't use all fields here, as the output would be overwhelming
+        // (these events are printed in the logs in junction and invoice).
+        final StringBuilder sb = new StringBuilder();
+        sb.append("DefaultBillingEvent");
+        sb.append("{type=").append(type);
+        sb.append(", effectiveDate=").append(effectiveDate);
+        sb.append(", planPhaseName=").append(planPhase.getName());
+        sb.append(", subscriptionId=").append(subscriptionId);
+        sb.append(", totalOrdering=").append(totalOrdering);
+        sb.append('}');
+        return sb.toString();
     }
 
-    private List<Usage> initializeUsage(final boolean isActive) {
-        List<Usage> result = ImmutableList.<Usage>of();
-        if (!isActive) {
-            return result;
-        }
-        if (planPhase != null && planPhase.getUsages().length > 0) {
-            result = Lists.newArrayList();
-            for (Usage usage : planPhase.getUsages()) {
-                result.add(usage);
-            }
-        }
-        return result;
-    }
 
-    @Override
-    public DateTime getCatalogEffectiveDate() {
-        return catalogEffectiveDate;
-    }
 }
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEventSet.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEventSet.java
index 0522fb1..8393d6c 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEventSet.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEventSet.java
@@ -28,6 +28,7 @@ import java.util.TreeSet;
 import java.util.UUID;
 
 import org.killbill.billing.catalog.api.BillingMode;
+import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Usage;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.junction.BillingEventSet;
@@ -78,7 +79,11 @@ public class DefaultBillingEventSet extends TreeSet<BillingEvent> implements Sor
         final Iterable<Usage> allUsages = Iterables.concat(Iterables.transform(this, new Function<BillingEvent, List<Usage>>() {
             @Override
             public List<Usage> apply(final BillingEvent input) {
-                return input.getUsages();
+                try {
+                    return input.getUsages();
+                } catch (final CatalogApiException e) {
+                    throw new IllegalStateException(String.format("Failed to retrieve usage section for billing event %s", input), e);
+                }
             }
         }));
         if (!allUsages.iterator().hasNext()) {
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
index a096e5e..a1c0391 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
@@ -21,6 +21,7 @@ package org.killbill.billing.junction.plumbing.billing;
 import java.math.BigDecimal;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -43,8 +44,6 @@ import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.entitlement.api.SubscriptionEventType;
-import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
-import org.killbill.billing.events.SubscriptionInternalEvent;
 import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.junction.BillingEventSet;
@@ -54,6 +53,7 @@ import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
+import org.killbill.billing.subscription.api.user.SubscriptionBillingEvent;
 import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.bcd.BillCycleDayCalculator;
@@ -108,23 +108,28 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
 
         if (found_AUTO_INVOICING_OFF) {
             result = new DefaultBillingEventSet(true, found_INVOICING_DRAFT, found_INVOICING_REUSE_DRAFT); // billing is off, we are done
-        } else {
-            final List<SubscriptionBaseBundle> bundles = subscriptionApi.getBundlesForAccount(accountId, context);
-
-            final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
-            result = new DefaultBillingEventSet(false, found_INVOICING_DRAFT, found_INVOICING_REUSE_DRAFT);
-            addBillingEventsForBundles(bundles, account, dryRunArguments, context, result, skippedSubscriptions, fullCatalog, tagsForAccount);
+            log.info("Account is AUTO_INVOICING_OFF: no billing event for accountId='{}'", accountId);
+            return result;
         }
 
+        final Map<UUID, List<SubscriptionBase>> subscriptionsForAccount = subscriptionApi.getSubscriptionsForAccount(fullCatalog, context);
+
+
+        final List<SubscriptionBaseBundle> bundles = subscriptionApi.getBundlesForAccount(accountId, context);
+        final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
+        result = new DefaultBillingEventSet(false, found_INVOICING_DRAFT, found_INVOICING_REUSE_DRAFT);
+        addBillingEventsForBundles(bundles, account, dryRunArguments, context, result, skippedSubscriptions, subscriptionsForAccount, fullCatalog, tagsForAccount);
         if (result.isEmpty()) {
             log.info("No billing event for accountId='{}'", accountId);
             return result;
         }
 
+
+
         // Pretty-print the events, before and after the blocking calculator does its magic
         final StringBuilder logStringBuilder = new StringBuilder("Computed billing events for accountId='").append(accountId).append("'");
         eventsToString(logStringBuilder, result);
-        if (blockCalculator.insertBlockingEvents(result, skippedSubscriptions, fullCatalog, context)) {
+        if (blockCalculator.insertBlockingEvents(result, skippedSubscriptions, subscriptionsForAccount, fullCatalog, context)) {
             logStringBuilder.append("\nBilling Events After Blocking");
             eventsToString(logStringBuilder, result);
         }
@@ -145,6 +150,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
                                             final InternalCallContext context,
                                             final DefaultBillingEventSet result,
                                             final Set<UUID> skipSubscriptionsSet,
+                                            final Map<UUID, List<SubscriptionBase>> subscriptionsForAccount,
                                             final Catalog catalog,
                                             final List<Tag> tagsForAccount) throws AccountApiException, CatalogApiException, SubscriptionBaseApiException {
         final int currentAccountBCD = accountApi.getBCD(context);
@@ -154,6 +160,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
                                    context,
                                    result,
                                    skipSubscriptionsSet,
+                                   subscriptionsForAccount,
                                    catalog,
                                    tagsForAccount,
                                    currentAccountBCD);
@@ -165,6 +172,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
                                             final InternalCallContext context,
                                             final DefaultBillingEventSet result,
                                             final Set<UUID> skipSubscriptionsSet,
+                                            final Map<UUID, List<SubscriptionBase>> subscriptionsForAccount,
                                             final Catalog catalog,
                                             final List<Tag> tagsForAccount,
                                             final int currentAccountBCD) throws AccountApiException, CatalogApiException, SubscriptionBaseApiException {
@@ -179,8 +187,6 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
             addBillingEventsForSubscription(account, subscriptions, null, currentAccountBCD, context, result, skipSubscriptionsSet, catalog);
         }
 
-        final Map<UUID, List<SubscriptionBase>> subscriptionsForAccount = subscriptionApi.getSubscriptionsForAccount(catalog, context);
-
         for (final SubscriptionBaseBundle bundle : bundles) {
             final DryRunArguments dryRunArgumentsForBundle = (dryRunArguments != null &&
                                                               dryRunArguments.getBundleId() != null &&
@@ -217,7 +223,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
             // Because we now have computed the real BCD, we need to re-compute the BillingEvents BCD for ACCOUNT alignments (see BillCycleDayCalculator#calculateBcdForAlignment).
             // The code could maybe be optimized (no need to re-run the full function?), but since it's run once per account, it's probably not worth it.
             result.clear();
-            addBillingEventsForBundles(bundles, account, dryRunArguments, context, result, skipSubscriptionsSet, catalog, tagsForAccount, accountBCDCandidate);
+            addBillingEventsForBundles(bundles, account, dryRunArguments, context, result, skipSubscriptionsSet, subscriptionsForAccount, catalog, tagsForAccount, accountBCDCandidate);
 
             final boolean dryRunMode = dryRunArguments != null;
             if (!dryRunMode) {
@@ -274,25 +280,25 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
 
         for (final SubscriptionBase subscription : subscriptions) {
 
-            final List<EffectiveSubscriptionInternalEvent> billingTransitions = subscriptionApi.getBillingTransitions(subscription, context);
+            final List<SubscriptionBillingEvent> billingTransitions = subscriptionApi.getSubscriptionBillingEvents(subscription, context);
             if (billingTransitions.isEmpty() ||
-                (billingTransitions.get(0).getTransitionType() != SubscriptionBaseTransitionType.CREATE &&
-                 billingTransitions.get(0).getTransitionType() != SubscriptionBaseTransitionType.TRANSFER)) {
+                (billingTransitions.get(0).getType() != SubscriptionBaseTransitionType.CREATE &&
+                 billingTransitions.get(0).getType() != SubscriptionBaseTransitionType.TRANSFER)) {
                 log.warn("Skipping billing events for subscription " + subscription.getId() + ": Does not start with a valid CREATE transition");
                 skipSubscriptionsSet.add(subscription.getId());
                 return;
             }
 
             Integer overridenBCD = null;
-            for (final EffectiveSubscriptionInternalEvent transition : billingTransitions) {
-                final BillingAlignment alignment = catalog.billingAlignment(getPlanPhaseSpecifierFromTransition(catalog, transition), transition.getEffectiveTransitionTime(), subscription.getStartDate());
+            for (final SubscriptionBillingEvent transition : billingTransitions) {
+                final BillingAlignment alignment = catalog.billingAlignment(getPlanPhaseSpecifierFromTransition(catalog, transition), transition.getEffectiveDate(), subscription.getStartDate());
 
                 //
                 // A BCD_CHANGE transition defines a new billCycleDayLocal for the subscription and this overrides whatever computation
                 // occurs below (which is based on billing alignment policy). Also multiple of those BCD_CHANGE transitions could occur,
                 // to define different intervals with different billing cycle days.
                 //
-                overridenBCD = transition.getNextBillCycleDayLocal() != null ? transition.getNextBillCycleDayLocal() : overridenBCD;
+                overridenBCD = transition.getBcdLocal() != null ? transition.getBcdLocal() : overridenBCD;
                 final int bcdLocal = overridenBCD != null ?
                                      overridenBCD :
                                      calculateBcdForTransition(alignment, bcdCache, baseSubscription, subscription, currentAccountBCD, context);
@@ -311,17 +317,9 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
         return BillCycleDayCalculator.calculateBcdForAlignment(bcdCache, subscription, baseSubscription, alignment, internalTenantContext, accountBillCycleDayLocal);
     }
 
-    private PlanPhaseSpecifier getPlanPhaseSpecifierFromTransition(final Catalog catalog, final SubscriptionInternalEvent transition) throws CatalogApiException {
-        final Plan prevPlan = (transition.getPreviousPlan() != null) ? catalog.findPlan(transition.getPreviousPlan(), transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
-        final Plan nextPlan = (transition.getNextPlan() != null) ? catalog.findPlan(transition.getNextPlan(), transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
-
-        final Plan plan = (transition.getTransitionType() != SubscriptionBaseTransitionType.CANCEL) ? nextPlan : prevPlan;
-
-        final PlanPhase prevPhase = prevPlan != null && transition.getPreviousPhase() != null ? prevPlan.findPhase(transition.getPreviousPhase()) : null;
-        final PlanPhase nextPhase = nextPlan != null && transition.getNextPhase() != null ? nextPlan.findPhase(transition.getNextPhase()) : null;
-
-        final PlanPhase phase = (transition.getTransitionType() != SubscriptionBaseTransitionType.CANCEL) ? nextPhase : prevPhase;
-
+    private PlanPhaseSpecifier getPlanPhaseSpecifierFromTransition(final Catalog catalog, final SubscriptionBillingEvent transition) throws CatalogApiException {
+        final Plan plan = catalog.findPlan(transition.getPlanName(), transition.getEffectiveDate(), transition.getLastChangePlanDate());
+        final PlanPhase phase =  plan.findPhase(transition.getPlanPhaseName());
         return new PlanPhaseSpecifier(plan.getName(), phase.getPhaseType());
     }
 
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
index e6e6daf..6cb0306 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
@@ -54,6 +54,8 @@ import org.killbill.billing.mock.MockEffectiveSubscriptionEvent;
 import org.killbill.billing.mock.MockSubscription;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
+import org.killbill.billing.subscription.api.user.SubscriptionBillingEvent;
+import org.killbill.billing.subscription.api.user.DefaultSubscriptionBillingEvent;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.util.api.TagApiException;
@@ -82,6 +84,7 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
     private static final UUID bunId = new UUID(2L, 0L);
     private static final String bunKey = bunId.toString();
 
+    private List<SubscriptionBillingEvent> billingTransitions;
     private List<EffectiveSubscriptionInternalEvent> effectiveSubscriptionTransitions;
     private SubscriptionBase subscription;
     private MockCatalog catalog;
@@ -97,6 +100,8 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
         Mockito.when(bundle.getId()).thenReturn(bunId);
         final List<SubscriptionBaseBundle> bundles = ImmutableList.<SubscriptionBaseBundle>of(bundle);
 
+
+        billingTransitions = new LinkedList<SubscriptionBillingEvent>();
         effectiveSubscriptionTransitions = new LinkedList<EffectiveSubscriptionInternalEvent>();
 
         final DateTime subscriptionStartDate = clock.getUTCNow().minusDays(3);
@@ -111,7 +116,7 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
         Mockito.when(subscriptionInternalApi.getSubscriptionFromId(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(subscription);
         Mockito.when(subscriptionInternalApi.getBundleFromId(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(bundle);
         Mockito.when(subscriptionInternalApi.getBaseSubscription(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(subscription);
-        Mockito.when(subscriptionInternalApi.getBillingTransitions(Mockito.<SubscriptionBase>any(),  Mockito.<InternalTenantContext>any())).thenReturn(effectiveSubscriptionTransitions);
+        Mockito.when(subscriptionInternalApi.getSubscriptionBillingEvents(Mockito.<SubscriptionBase>any(), Mockito.<InternalTenantContext>any())).thenReturn(billingTransitions);
         Mockito.when(subscriptionInternalApi.getAllTransitions(Mockito.<SubscriptionBase>any(), Mockito.<InternalTenantContext>any())).thenReturn(effectiveSubscriptionTransitions);
 
         final DefaultVersionedCatalog versionedCatalog = catalogService.getFullCatalog(true, true, internalCallContext);
@@ -260,13 +265,13 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
         }
 
         if (recurringPrice != null) {
-            Assert.assertEquals(recurringPrice.getPrice(Currency.USD), event.getRecurringPrice(null));
+            Assert.assertEquals(recurringPrice.getPrice(Currency.USD), event.getRecurringPrice(time));
         } else {
-            assertNull(event.getRecurringPrice(null));
+            assertNull(event.getRecurringPrice(time));
         }
 
         Assert.assertEquals(BCD, event.getBillCycleDayLocal());
-        Assert.assertEquals(id, event.getSubscription().getId());
+        Assert.assertEquals(id, event.getSubscriptionId());
         Assert.assertEquals(time.getDayOfMonth(), event.getEffectiveDate().getDayOfMonth());
         Assert.assertEquals(nextPhase, event.getPlanPhase());
         Assert.assertEquals(nextPlan, event.getPlan());
@@ -300,6 +305,8 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
                 SubscriptionBaseTransitionType.CREATE, 1, null, 1L, 2L, null);
 
         effectiveSubscriptionTransitions.add(t);
+        billingTransitions.add(new DefaultSubscriptionBillingEvent(SubscriptionBaseTransitionType.CREATE, nextPlan.getName(), nextPhase.getName(), now, 1L, now, null));
+
         return now;
     }
 }
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java
index 58d299b..5f199f1 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java
@@ -20,10 +20,12 @@ package org.killbill.billing.junction.plumbing.billing;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.UUID;
@@ -32,9 +34,13 @@ import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.killbill.billing.account.api.Account;
+import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.DefaultUsage;
+import org.killbill.billing.catalog.DefaultVersionedCatalog;
+import org.killbill.billing.catalog.MockCatalog;
 import org.killbill.billing.catalog.MockPlan;
 import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.InternationalPrice;
@@ -49,6 +55,7 @@ import org.killbill.billing.junction.DefaultBlockingState;
 import org.killbill.billing.junction.JunctionTestSuiteNoDB;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
+import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
@@ -74,6 +81,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     private SubscriptionBase subscription2;
     private SubscriptionBase subscription3;
     private SubscriptionBase subscription4;
+    private Map<UUID, List<SubscriptionBase>> subscriptionsForAccount;
+
+    private Catalog catalog;
 
     @BeforeMethod(groups = "fast")
     public void beforeMethod() throws Exception {
@@ -83,6 +93,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
 
         super.beforeMethod();
         account = Mockito.mock(Account.class);
+
+        subscriptionsForAccount = new HashMap<>();
+
         subscription1 = Mockito.mock(SubscriptionBase.class);
         subscription2 = Mockito.mock(SubscriptionBase.class);
         subscription3 = Mockito.mock(SubscriptionBase.class);
@@ -91,12 +104,27 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         Mockito.when(subscription1.getBundleId()).thenReturn(bundleId1);
         Mockito.when(subscription2.getBundleId()).thenReturn(bundleId1);
         Mockito.when(subscription3.getBundleId()).thenReturn(bundleId1);
+        final List<SubscriptionBase> bundleSubscriptions1 = new ArrayList<>();
+        bundleSubscriptions1.add(subscription1);
+        bundleSubscriptions1.add(subscription2);
+        bundleSubscriptions1.add(subscription3);
+        subscriptionsForAccount.put(bundleId1, bundleSubscriptions1);
+
         Mockito.when(subscription4.getBundleId()).thenReturn(bundleId2);
+        final List<SubscriptionBase> bundleSubscriptions2 = new ArrayList<>();
+        bundleSubscriptions1.add(subscription4);
+        subscriptionsForAccount.put(bundleId2, bundleSubscriptions2);
+
+
         Mockito.when(subscription1.getId()).thenReturn(UUID.randomUUID());
         Mockito.when(subscription2.getId()).thenReturn(UUID.randomUUID());
         Mockito.when(subscription3.getId()).thenReturn(UUID.randomUUID());
         Mockito.when(subscription4.getId()).thenReturn(UUID.randomUUID());
 
+
+        catalog = Mockito.mock(Catalog.class);
+
+
         ((MockBlockingStateDao) blockingStateDao).clear();
     }
 
@@ -114,10 +142,10 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     public void testInsertBlockingEventsForBundle() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
 
-        final BillingEvent A = createRealEvent(now.minusDays(1).minusHours(1), subscription1);
-        final BillingEvent B = createRealEvent(now.minusDays(1), subscription2);
-        final BillingEvent C = createRealEvent(now.plusDays(1), subscription2);
-        final BillingEvent D = createRealEvent(now.plusDays(3), subscription3);
+        final BillingEvent A = createRealEvent(subscription1, now.minusDays(1).minusHours(1));
+        final BillingEvent B = createRealEvent(subscription2, now.minusDays(1));
+        final BillingEvent C = createRealEvent(subscription2, now.plusDays(1));
+        final BillingEvent D = createRealEvent(subscription3, now.plusDays(3));
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
         billingEvents.add(A);
         billingEvents.add(B);
@@ -131,7 +159,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
                                                                                                                         blockingState2, Optional.<UUID>absent()),
                                                                          internalCallContext);
 
-        blockingCalculator.insertBlockingEvents(billingEvents, new HashSet<UUID>(), catalogInternalApi.getFullCatalog(true, true, internalCallContext), internalCallContext);
+        blockingCalculator.insertBlockingEvents(billingEvents, new HashSet<UUID>(), subscriptionsForAccount, catalog, internalCallContext);
 
         assertEquals(billingEvents.size(), 7);
 
@@ -161,7 +189,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, null));
-        billingEvents.add(createRealEvent(now.minusDays(1), subscription1));
+        billingEvents.add(createRealEvent(subscription1, now.minusDays(1)));
 
         final SortedSet<BillingEvent> results = blockingCalculator.eventsToRemove(disabledDuration, billingEvents);
 
@@ -177,8 +205,8 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, null));
-        final BillingEvent e1 = createRealEvent(now.minusDays(1), subscription1);
-        final BillingEvent e2 = createRealEvent(now.plusDays(1), subscription1);
+        final BillingEvent e1 = createRealEvent(subscription1, now.minusDays(1));
+        final BillingEvent e2 = createRealEvent(subscription1, now.plusDays(1));
         billingEvents.add(e1);
         billingEvents.add(e2);
 
@@ -197,7 +225,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, null));
-        final BillingEvent e1 = createRealEvent(now.plusDays(1), subscription1);
+        final BillingEvent e1 = createRealEvent(subscription1, now.plusDays(1));
         billingEvents.add(e1);
 
         final SortedSet<BillingEvent> results = blockingCalculator.eventsToRemove(disabledDuration, billingEvents);
@@ -215,7 +243,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, null));
-        final BillingEvent e1 = createRealEvent(now, subscription1);
+        final BillingEvent e1 = createRealEvent(subscription1, now);
         billingEvents.add(e1);
 
         final SortedSet<BillingEvent> results = blockingCalculator.eventsToRemove(disabledDuration, billingEvents);
@@ -233,7 +261,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
-        final BillingEvent e1 = createRealEvent(now.minusDays(1), subscription1);
+        final BillingEvent e1 = createRealEvent(subscription1, now.minusDays(1));
         billingEvents.add(e1);
 
         final SortedSet<BillingEvent> results = blockingCalculator.eventsToRemove(disabledDuration, billingEvents);
@@ -250,8 +278,8 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
-        final BillingEvent e1 = createRealEvent(now.minusDays(1), subscription1);
-        final BillingEvent e2 = createRealEvent(now.plusDays(1), subscription1);
+        final BillingEvent e1 = createRealEvent(subscription1, now.minusDays(1));
+        final BillingEvent e2 = createRealEvent(subscription1, now.plusDays(1));
         billingEvents.add(e1);
         billingEvents.add(e2);
 
@@ -270,9 +298,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
-        final BillingEvent e1 = createRealEvent(now.minusDays(1), subscription1);
-        final BillingEvent e2 = createRealEvent(now.plusDays(1), subscription1);
-        final BillingEvent e3 = createRealEvent(now.plusDays(3), subscription1);
+        final BillingEvent e1 = createRealEvent(subscription1, now.minusDays(1));
+        final BillingEvent e2 = createRealEvent(subscription1, now.plusDays(1));
+        final BillingEvent e3 = createRealEvent(subscription1, now.plusDays(3));
         billingEvents.add(e1);
         billingEvents.add(e2);
         billingEvents.add(e3);
@@ -292,7 +320,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
-        final BillingEvent e2 = createRealEvent(now.plusDays(1), subscription1);
+        final BillingEvent e2 = createRealEvent(subscription1, now.plusDays(1));
         billingEvents.add(e2);
 
         final SortedSet<BillingEvent> results = blockingCalculator.eventsToRemove(disabledDuration, billingEvents);
@@ -311,8 +339,8 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
 
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
 
-        final BillingEvent e2 = createRealEvent(now.plusDays(1), subscription1);
-        final BillingEvent e3 = createRealEvent(now.plusDays(3), subscription1);
+        final BillingEvent e2 = createRealEvent(subscription1, now.plusDays(1));
+        final BillingEvent e3 = createRealEvent(subscription1, now.plusDays(3));
         billingEvents.add(e2);
         billingEvents.add(e3);
 
@@ -332,7 +360,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
 
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
 
-        final BillingEvent e3 = createRealEvent(now.plusDays(3), subscription1);
+        final BillingEvent e3 = createRealEvent(subscription1, now.plusDays(3));
 
         billingEvents.add(e3);
 
@@ -350,14 +378,14 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, null));
-        billingEvents.add(createRealEvent(now.minusDays(1), subscription1));
+        billingEvents.add(createRealEvent(subscription1, now.minusDays(1)));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalogInternalApi.getFullCatalog(true, true, internalCallContext),  internalCallContext);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalog,  internalCallContext);
 
         assertEquals(results.size(), 1);
         assertEquals(results.first().getEffectiveDate(), now);
         assertNull(results.first().getFixedPrice());
-        assertNull(results.first().getRecurringPrice(null));
+        assertNull(results.first().getRecurringPrice(now));
         assertEquals(results.first().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
         assertEquals(results.first().getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
     }
@@ -371,15 +399,15 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, null));
-        billingEvents.add(createRealEvent(now.minusDays(1), subscription1));
-        billingEvents.add(createRealEvent(now.plusDays(1), subscription1));
+        billingEvents.add(createRealEvent(subscription1, now.minusDays(1)));
+        billingEvents.add(createRealEvent(subscription1, now.plusDays(1)));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalogInternalApi.getFullCatalog(true, true, internalCallContext), internalCallContext);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalog, internalCallContext);
 
         assertEquals(results.size(), 1);
         assertEquals(results.first().getEffectiveDate(), now);
         assertNull(results.first().getFixedPrice());
-        assertNull(results.first().getRecurringPrice(null));
+        assertNull(results.first().getRecurringPrice(now));
         assertEquals(results.first().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
         assertEquals(results.first().getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
     }
@@ -393,9 +421,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, null));
-        billingEvents.add(createRealEvent(now.plusDays(1), subscription1));
+        billingEvents.add(createRealEvent(subscription1, now.plusDays(1)));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalogInternalApi.getFullCatalog(true, true, internalCallContext), internalCallContext);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalog, internalCallContext);
 
         assertEquals(results.size(), 0);
     }
@@ -409,9 +437,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, null));
-        billingEvents.add(createRealEvent(now, subscription1));
+        billingEvents.add(createRealEvent(subscription1, now));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalogInternalApi.getFullCatalog(true, true, internalCallContext), internalCallContext);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalog, internalCallContext);
 
         assertEquals(results.size(), 0);
     }
@@ -425,19 +453,19 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
-        billingEvents.add(createRealEvent(now.minusDays(1), subscription1));
+        billingEvents.add(createRealEvent(subscription1, now.minusDays(1)));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalogInternalApi.getFullCatalog(true, true, internalCallContext), internalCallContext);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalog, internalCallContext);
 
 
         assertEquals(results.size(), 2);
         assertEquals(results.first().getEffectiveDate(), now);
         assertNull(results.first().getFixedPrice());
-        assertNull(results.first().getRecurringPrice(null));
+        assertNull(results.first().getRecurringPrice(now));
         assertEquals(results.first().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
         assertEquals(results.first().getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
         assertEquals(results.last().getEffectiveDate(), now.plusDays(2));
-        assertEquals(results.last().getRecurringPrice(null), billingEvents.first().getRecurringPrice(null));
+        assertEquals(results.last().getRecurringPrice(now), billingEvents.first().getRecurringPrice(now));
         assertEquals(results.last().getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
     }
 
@@ -450,19 +478,19 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
-        billingEvents.add(createRealEvent(now.minusDays(1), subscription1));
-        billingEvents.add(createRealEvent(now.plusDays(1), subscription1));
+        billingEvents.add(createRealEvent(subscription1, now.minusDays(1)));
+        billingEvents.add(createRealEvent(subscription1, now.plusDays(1)));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalogInternalApi.getFullCatalog(true, true, internalCallContext), internalCallContext);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalog, internalCallContext);
 
         assertEquals(results.size(), 2);
         assertEquals(results.first().getEffectiveDate(), now);
         assertNull(results.first().getFixedPrice());
-        assertNull(results.first().getRecurringPrice(null));
+        assertNull(results.first().getRecurringPrice(now));
         assertEquals(results.first().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
         assertEquals(results.first().getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
         assertEquals(results.last().getEffectiveDate(), now.plusDays(2));
-        assertEquals(results.last().getRecurringPrice(null), billingEvents.first().getRecurringPrice(null));
+        assertEquals(results.last().getRecurringPrice(now), billingEvents.first().getRecurringPrice(now));
         assertEquals(results.last().getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
     }
 
@@ -475,20 +503,20 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
-        billingEvents.add(createRealEvent(now.minusDays(1), subscription1));
-        billingEvents.add(createRealEvent(now.plusDays(1), subscription1));
-        billingEvents.add(createRealEvent(now.plusDays(3), subscription1));
+        billingEvents.add(createRealEvent(subscription1, now.minusDays(1)));
+        billingEvents.add(createRealEvent(subscription1, now.plusDays(1)));
+        billingEvents.add(createRealEvent(subscription1, now.plusDays(3)));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalogInternalApi.getFullCatalog(true, true, internalCallContext), internalCallContext);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalog, internalCallContext);
 
         assertEquals(results.size(), 2);
         assertEquals(results.first().getEffectiveDate(), now);
         assertNull(results.first().getFixedPrice());
-        assertNull(results.first().getRecurringPrice(null));
+        assertNull(results.first().getRecurringPrice(now));
         assertEquals(results.first().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
         assertEquals(results.first().getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
         assertEquals(results.last().getEffectiveDate(), now.plusDays(2));
-        assertEquals(results.last().getRecurringPrice(null), billingEvents.first().getRecurringPrice(null));
+        assertEquals(results.last().getRecurringPrice(now), billingEvents.first().getRecurringPrice(now));
         assertEquals(results.last().getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
     }
 
@@ -501,13 +529,13 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
-        billingEvents.add(createRealEvent(now.plusDays(1), subscription1));
+        billingEvents.add(createRealEvent(subscription1, now.plusDays(1)));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalogInternalApi.getFullCatalog(true, true, internalCallContext), internalCallContext);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalog, internalCallContext);
 
         assertEquals(results.size(), 1);
         assertEquals(results.last().getEffectiveDate(), now.plusDays(2));
-        assertEquals(results.last().getRecurringPrice(null), billingEvents.first().getRecurringPrice(null));
+        assertEquals(results.last().getRecurringPrice(now), billingEvents.first().getRecurringPrice(now));
         assertEquals(results.last().getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
     }
 
@@ -520,13 +548,13 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
-        billingEvents.add(createRealEvent(now.plusDays(1), subscription1));
+        billingEvents.add(createRealEvent(subscription1, now.plusDays(1)));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalogInternalApi.getFullCatalog(true, true, internalCallContext), internalCallContext);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalog, internalCallContext);
 
         assertEquals(results.size(), 1);
         assertEquals(results.last().getEffectiveDate(), now.plusDays(2));
-        assertEquals(results.last().getRecurringPrice(null), billingEvents.first().getRecurringPrice(null));
+        assertEquals(results.last().getRecurringPrice(now), billingEvents.first().getRecurringPrice(now));
         assertEquals(results.last().getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
     }
 
@@ -539,9 +567,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
 
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
-        billingEvents.add(createRealEvent(now.plusDays(3), subscription1));
+        billingEvents.add(createRealEvent(subscription1, now.plusDays(3)));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalogInternalApi.getFullCatalog(true, true, internalCallContext), internalCallContext);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, catalog, internalCallContext);
 
         assertEquals(results.size(), 0);
     }
@@ -552,10 +580,10 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
 
         final SortedSet<BillingEvent> events = new TreeSet<BillingEvent>();
 
-        events.add(createRealEvent(now.minusDays(10), subscription1));
-        events.add(createRealEvent(now.minusDays(6), subscription1));
-        events.add(createRealEvent(now.minusDays(5), subscription1));
-        events.add(createRealEvent(now.minusDays(1), subscription1));
+        events.add(createRealEvent(subscription1, now.minusDays(10)));
+        events.add(createRealEvent(subscription1, now.minusDays(6)));
+        events.add(createRealEvent(subscription1, now.minusDays(5)));
+        events.add(createRealEvent(subscription1, now.minusDays(1)));
 
         final BillingEvent minus11 = blockingCalculator.precedingBillingEventForSubscription(now.minusDays(11), events);
         assertNull(minus11);
@@ -566,78 +594,38 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
 
     }
 
-    protected BillingEvent createRealEvent(final DateTime effectiveDate, final SubscriptionBase subscription) {
-        return createRealEvent(effectiveDate, subscription, SubscriptionBaseTransitionType.CHANGE);
-    }
-
-    protected BillingEvent createRealEvent(final DateTime effectiveDate, final SubscriptionBase subscription, final SubscriptionBaseTransitionType type)  {
-        try {
-
-            final Integer billCycleDay = 1;
-            final Plan plan = new MockPlan();
-            final Currency currency = Currency.USD;
-            final String description = "";
-            final BillingPeriod billingPeriod = BillingPeriod.MONTHLY;
-            final Long totalOrdering = 0L;
-            final DateTimeZone tz = DateTimeZone.UTC;
-
-
-            final PlanPhase planPhase = Mockito.mock(PlanPhase.class);
-
-            final InternationalPrice recurringPrice = Mockito.mock(InternationalPrice.class);
-
-            Mockito.when(recurringPrice.getPrice(Mockito.<Currency>any())).thenReturn(BigDecimal.TEN);
-            final Recurring recurring = Mockito.mock(Recurring.class);
-            Mockito.when(recurring.getRecurringPrice()).thenReturn(recurringPrice);
-            Mockito.when(planPhase.getRecurring()).thenReturn(recurring);
-            Mockito.when(planPhase.getUsages()).thenReturn(new DefaultUsage[0]);
-
-            final BigDecimal fixedPrice = BigDecimal.TEN;
-
-            return new DefaultBillingEvent(subscription, effectiveDate, true, plan, planPhase, fixedPrice,
-                                           currency,
-                                           billingPeriod, billCycleDay,
-                                           description, totalOrdering, type, null, false);
-
-        } catch (final CatalogApiException e) {
-            Assert.fail("", e);
-        }
-        throw new IllegalStateException();
-    }
-
     @Test(groups = "fast")
     public void testFilter() {
         final SortedSet<BillingEvent> events = new TreeSet<BillingEvent>();
 
-        events.add(createBillingEvent(subscription1));
-        events.add(createBillingEvent(subscription1));
-        events.add(createBillingEvent(subscription1));
-        events.add(createBillingEvent(subscription2));
+        events.add(createBillingEvent(subscription1, 1L));
+        events.add(createBillingEvent(subscription1, 2L));
+        events.add(createBillingEvent(subscription1, 3L));
+        events.add(createBillingEvent(subscription2, 4L));
 
         final SortedSet<BillingEvent> result1 = blockingCalculator.filter(events, subscription1);
         final SortedSet<BillingEvent> result2 = blockingCalculator.filter(events, subscription2);
         final SortedSet<BillingEvent> result3 = blockingCalculator.filter(events, subscription3);
 
         assertEquals(result1.size(), 3);
-        assertEquals(result1.first().getSubscription(), subscription1);
-        assertEquals(result1.last().getSubscription(), subscription1);
         assertEquals(result2.size(), 1);
-        assertEquals(result2.first().getSubscription(), subscription2);
         assertEquals(result3.size(), 0);
     }
 
     @Test(groups = "fast")
     public void testCreateNewDisableEvent() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
-        final BillingEvent event = new MockBillingEvent();
 
-        final BillingEvent result = blockingCalculator.createNewDisableEvent(now, event, null);
+
+        final BillingEvent event = createRealEvent(subscription1, now);
+
+        final BillingEvent result = blockingCalculator.createNewDisableEvent(now, event, catalog);
         assertEquals(result.getBillCycleDayLocal(), event.getBillCycleDayLocal());
         assertEquals(result.getEffectiveDate(), now);
         assertEquals(result.getPlanPhase(), event.getPlanPhase());
         assertEquals(result.getPlan(), event.getPlan());
         assertNull(result.getFixedPrice());
-        assertNull(result.getRecurringPrice(null));
+        assertNull(result.getRecurringPrice(now));
         assertEquals(result.getCurrency(), event.getCurrency());
         assertEquals(result.getDescription(), "");
         assertEquals(result.getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
@@ -649,15 +637,15 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     @Test(groups = "fast")
     public void testCreateNewReenableEvent() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
-        final BillingEvent event = new MockBillingEvent();
+        final BillingEvent event = createRealEvent(subscription1, now);
 
-        final BillingEvent result = blockingCalculator.createNewReenableEvent(now, event, null, internalCallContext);
+        final BillingEvent result = blockingCalculator.createNewReenableEvent(now, event, catalog, internalCallContext);
         assertEquals(result.getBillCycleDayLocal(), event.getBillCycleDayLocal());
         assertEquals(result.getEffectiveDate(), now);
         assertEquals(result.getPlanPhase(), event.getPlanPhase());
         assertEquals(result.getPlan(), event.getPlan());
         assertEquals(result.getFixedPrice(), event.getFixedPrice());
-        assertEquals(result.getRecurringPrice(null), event.getRecurringPrice(null));
+        assertEquals(result.getRecurringPrice(now), event.getRecurringPrice(now));
         assertEquals(result.getCurrency(), event.getCurrency());
         assertEquals(result.getDescription(), "");
         assertEquals(result.getBillingPeriod(), event.getBillingPeriod());
@@ -666,38 +654,6 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertEquals(result.getTotalOrdering(), (Long) (BlockingCalculator.getGlobalTotalOrder().get() - 1));
     }
 
-    private class MockBillingEvent extends DefaultBillingEvent {
-
-        public MockBillingEvent() throws CatalogApiException {
-            super(subscription1, clock.getUTCNow(), true, null, null, BigDecimal.ZERO, Currency.USD, BillingPeriod.ANNUAL,
-                  4, "", 3L, SubscriptionBaseTransitionType.CREATE, null, false);
-        }
-    }
-
-    @Test(groups = "fast")
-    public void testCreateBundleSubscriptionMap() {
-        final SortedSet<BillingEvent> events = new TreeSet<BillingEvent>();
-        events.add(createBillingEvent(subscription1));
-        events.add(createBillingEvent(subscription2));
-        events.add(createBillingEvent(subscription3));
-        events.add(createBillingEvent(subscription4));
-
-        final Hashtable<UUID, List<SubscriptionBase>> map = blockingCalculator.createBundleSubscriptionMap(events);
-
-        assertNotNull(map);
-        assertEquals(map.keySet().size(), 2);
-        assertEquals(map.get(bundleId1).size(), 3);
-        assertEquals(map.get(bundleId2).size(), 1);
-
-    }
-
-    private BillingEvent createBillingEvent(final SubscriptionBase subscription) {
-        final BillingEvent result = Mockito.mock(BillingEvent.class);
-        Mockito.when(result.getSubscription()).thenReturn(subscription);
-        Mockito.when(result.compareTo(Mockito.<BillingEvent>any())).thenReturn(1);
-        return result;
-    }
-
     @Test(groups = "fast")
     public void testCreateDisablePairs() {
         List<BlockingState> blockingEvents;
@@ -833,9 +789,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     @Test(groups = "fast")
     public void testSimpleWithClearBlockingDuration() throws Exception {
 
-        final BillingEvent trial = createRealEvent(new LocalDate(2012, 5, 1).toDateTimeAtStartOfDay(DateTimeZone.UTC), subscription1, SubscriptionBaseTransitionType.CREATE);
-        final BillingEvent phase = createRealEvent(new LocalDate(2012, 5, 31).toDateTimeAtStartOfDay(DateTimeZone.UTC), subscription1, SubscriptionBaseTransitionType.PHASE);
-        final BillingEvent upgrade = createRealEvent(new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC), subscription1, SubscriptionBaseTransitionType.CHANGE);
+        final BillingEvent trial = createRealEvent(subscription1, new LocalDate(2012, 5, 1).toDateTimeAtStartOfDay(DateTimeZone.UTC), SubscriptionBaseTransitionType.CREATE);
+        final BillingEvent phase = createRealEvent(subscription1, new LocalDate(2012, 5, 31).toDateTimeAtStartOfDay(DateTimeZone.UTC), SubscriptionBaseTransitionType.PHASE);
+        final BillingEvent upgrade = createRealEvent(subscription1, new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC), SubscriptionBaseTransitionType.CHANGE);
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
         billingEvents.add(trial);
         billingEvents.add(phase);
@@ -852,7 +808,8 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
                                                                                                                         blockingState4, Optional.<UUID>absent()),
                                                                          internalCallContext);
 
-        blockingCalculator.insertBlockingEvents(billingEvents, new HashSet<UUID>(), catalogInternalApi.getFullCatalog(true, true, internalCallContext), internalCallContext);
+
+        blockingCalculator.insertBlockingEvents(billingEvents, new HashSet<UUID>(), subscriptionsForAccount, catalog, internalCallContext);
 
         assertEquals(billingEvents.size(), 5);
         final List<BillingEvent> events = new ArrayList<BillingEvent>(billingEvents);
@@ -866,4 +823,63 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertEquals(events.get(3).getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
         assertEquals(events.get(4).getEffectiveDate(), new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC));
         assertEquals(events.get(4).getTransitionType(), SubscriptionBaseTransitionType.CHANGE);    }
+
+
+    private BillingEvent createBillingEvent(final SubscriptionBase subscription, final Long totalOrdering) {
+        return createRealEvent( subscription, new DateTime(), SubscriptionBaseTransitionType.CREATE, totalOrdering);
+    }
+
+    protected BillingEvent createRealEvent(final SubscriptionBase subscription, final DateTime effectiveDate) {
+        return createRealEvent(subscription, effectiveDate, SubscriptionBaseTransitionType.CHANGE);
+    }
+
+    protected BillingEvent createRealEvent(final SubscriptionBase subscription, final DateTime effectiveDate, final SubscriptionBaseTransitionType type) {
+        return createRealEvent(subscription, effectiveDate, type, 0L);
+    }
+
+    private BillingEvent createRealEvent(final SubscriptionBase subscription, final DateTime effectiveDate, final SubscriptionBaseTransitionType type, final Long totalOrdering)  {
+        try {
+
+            final Integer billCycleDay = 1;
+            final Plan plan = new MockPlan();
+            final Currency currency = Currency.USD;
+            final String description = "";
+            final BillingPeriod billingPeriod = BillingPeriod.MONTHLY;
+
+            final PlanPhase planPhase = Mockito.mock(PlanPhase.class);
+
+            final InternationalPrice recurringPrice = Mockito.mock(InternationalPrice.class);
+
+            Mockito.when(recurringPrice.getPrice(Mockito.<Currency>any())).thenReturn(BigDecimal.TEN);
+            final Recurring recurring = Mockito.mock(Recurring.class);
+            Mockito.when(recurring.getRecurringPrice()).thenReturn(recurringPrice);
+            Mockito.when(planPhase.getRecurring()).thenReturn(recurring);
+            Mockito.when(planPhase.getUsages()).thenReturn(new DefaultUsage[0]);
+
+            Mockito.when(catalog.findPhase(Mockito.<String>any(), Mockito.<DateTime>any(), Mockito.<DateTime>any())).thenReturn(planPhase);
+
+            final BigDecimal fixedPrice = BigDecimal.TEN;
+
+            return new DefaultBillingEvent(subscription.getId(),
+                                           subscription.getBundleId(),
+                                           effectiveDate,
+                                           plan,
+                                           planPhase,
+                                           fixedPrice,
+                                           currency,
+                                           billingPeriod,
+                                           effectiveDate,
+                                           billCycleDay,
+                                           description,
+                                           totalOrdering,
+                                           type,
+                                           false,
+                                           catalog);
+
+        } catch (final CatalogApiException e) {
+            Assert.fail("", e);
+        }
+        throw new IllegalStateException();
+    }
+
 }
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultBillingEvent.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultBillingEvent.java
index 05ff9dd..10e178f 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultBillingEvent.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultBillingEvent.java
@@ -31,7 +31,6 @@ import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import org.killbill.billing.account.api.Account;
 import org.killbill.billing.catalog.DefaultPrice;
 import org.killbill.billing.catalog.MockInternationalPrice;
 import org.killbill.billing.catalog.MockPlan;
@@ -44,7 +43,6 @@ import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.junction.JunctionTestSuiteNoDB;
-import org.killbill.billing.mock.MockAccountBuilder;
 import org.killbill.billing.junction.BillingEvent;
 
 public class TestDefaultBillingEvent extends JunctionTestSuiteNoDB {
@@ -186,10 +184,10 @@ public class TestDefaultBillingEvent extends JunctionTestSuiteNoDB {
         final Plan shotgun = new MockPlan();
         final PlanPhase shotgunMonthly = createMockMonthlyPlanPhase(null, BigDecimal.ZERO, PhaseType.TRIAL);
 
-        return new DefaultBillingEvent(sub, effectiveDate, true,
+        return new DefaultBillingEvent(sub.getId(), sub.getBundleId(), effectiveDate,
                                        shotgun, shotgunMonthly, BigDecimal.ZERO,
-                                       Currency.USD, BillingPeriod.NO_BILLING_PERIOD, billCycleDay,
-                                       "Test Event 1", totalOrdering, type, null, false);
+                                       Currency.USD, BillingPeriod.NO_BILLING_PERIOD, effectiveDate, billCycleDay,
+                                       "Test Event 1", totalOrdering, type, false, null);
     }
 
     private MockPlanPhase createMockMonthlyPlanPhase(@Nullable final BigDecimal recurringRate,
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index 0e830df..1fddc01 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -53,6 +53,7 @@ import org.killbill.billing.subscription.api.SubscriptionBaseApiService;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOns;
 import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOnsSpecifier;
+import org.killbill.billing.subscription.api.user.SubscriptionBillingEvent;
 import org.killbill.billing.subscription.api.user.DefaultEffectiveSubscriptionEvent;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseBundle;
@@ -349,9 +350,8 @@ public class DefaultSubscriptionInternalApi extends DefaultSubscriptionBaseCreat
     }
 
     @Override
-    public List<EffectiveSubscriptionInternalEvent> getBillingTransitions(final SubscriptionBase subscription, final InternalTenantContext context) {
-        final List<SubscriptionBaseTransition> transitions = ((DefaultSubscriptionBase) subscription).getBillingTransitions();
-        return convertEffectiveSubscriptionInternalEventFromSubscriptionTransitions(subscription, context, transitions);
+    public List<SubscriptionBillingEvent> getSubscriptionBillingEvents(final SubscriptionBase subscription, final InternalTenantContext context) {
+        return((DefaultSubscriptionBase) subscription).getBillingTransitions();
     }
 
     @Override
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 fc09a99..4aaf947 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
@@ -524,26 +524,40 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         return it.hasNext() ? ((SubscriptionBaseTransitionData) it.next()).getTotalOrdering() : -1L;
     }
 
-    public List<SubscriptionBaseTransition> getBillingTransitions() {
+    public List<SubscriptionBillingEvent> getBillingTransitions() {
 
         if (transitions == null) {
             return Collections.emptyList();
         }
-        final List<SubscriptionBaseTransition> result = new ArrayList<SubscriptionBaseTransition>();
+        final List<SubscriptionBillingEvent> result = new ArrayList<SubscriptionBillingEvent>();
         final SubscriptionBaseTransitionDataIterator it = new SubscriptionBaseTransitionDataIterator(
                 clock, transitions, Order.ASC_FROM_PAST,
                 Visibility.ALL, TimeLimit.ALL);
         // Remove anything prior to first CREATE
         boolean foundInitialEvent = false;
+
+        DateTime lastPlanChangeTime = null;
         while (it.hasNext()) {
-            final SubscriptionBaseTransitionData curTransition = (SubscriptionBaseTransitionData) it.next();
+            final SubscriptionBaseTransitionData cur = (SubscriptionBaseTransitionData) it.next();
             if (!foundInitialEvent) {
-                foundInitialEvent = curTransition.getEventType() == EventType.API_USER &&
-                                    (curTransition.getApiEventType() == ApiEventType.CREATE ||
-                                     curTransition.getApiEventType() == ApiEventType.TRANSFER);
+                foundInitialEvent = cur.getEventType() == EventType.API_USER &&
+                                    (cur.getApiEventType() == ApiEventType.CREATE ||
+                                     cur.getApiEventType() == ApiEventType.TRANSFER);
+                lastPlanChangeTime = cur.getEffectiveTransitionTime();
             }
+
             if (foundInitialEvent) {
-                result.add(curTransition);
+
+                if (cur.getEventType() == EventType.API_USER &&
+                                    cur.getApiEventType() == ApiEventType.CHANGE) {
+                    lastPlanChangeTime = cur.getEffectiveTransitionTime();
+                }
+
+                final boolean isActive = cur.getTransitionType() != SubscriptionBaseTransitionType.CANCEL;
+                final String planName = isActive ? cur.getNextPlan().getName() : cur.getPreviousPlan().getName();
+                final String planPhaseName = isActive ?  cur.getNextPhase().getName() : cur.getPreviousPhase().getName();
+                final SubscriptionBillingEvent billingTransition = new DefaultSubscriptionBillingEvent(cur.getTransitionType(), planName, planPhaseName, cur.getEffectiveTransitionTime(), cur.getTotalOrdering(), lastPlanChangeTime, cur.getNextBillingCycleDayLocal());
+                result.add(billingTransition);
             }
         }
         return result;
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBillingEvent.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBillingEvent.java
new file mode 100644
index 0000000..51f271a
--- /dev/null
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBillingEvent.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014-2019 Groupon, Inc
+ * Copyright 2014-2019 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.subscription.api.user;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
+
+public class DefaultSubscriptionBillingEvent implements SubscriptionBillingEvent {
+
+    private final SubscriptionBaseTransitionType type;
+    private final String planName;
+    private final String planPhaseName;
+    private final DateTime effectiveDate;
+    private final Long totalOrdering;
+    private final DateTime lastChangePlanDate;
+    private final Integer bcdLocal;
+
+    public DefaultSubscriptionBillingEvent(final SubscriptionBaseTransitionType type, final String planName, final String planPhaseName, final DateTime effectiveDate, final Long totalOrdering, final DateTime lastChangePlanDate, final Integer bcdLocal) {
+        this.type = type;
+        this.planName = planName;
+        this.planPhaseName = planPhaseName;
+        this.effectiveDate = effectiveDate;
+        this.totalOrdering = totalOrdering;
+        this.lastChangePlanDate = lastChangePlanDate;
+        this.bcdLocal = bcdLocal;
+    }
+
+    @Override
+    public SubscriptionBaseTransitionType getType() {
+        return type;
+    }
+
+    @Override
+    public String getPlanName() {
+        return planName;
+    }
+
+    @Override
+    public String getPlanPhaseName() {
+        return planPhaseName;
+    }
+
+    @Override
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    @Override
+    public Long getTotalOrdering() {
+        return totalOrdering;
+    }
+
+    @Override
+    public DateTime getLastChangePlanDate() {
+        return lastChangePlanDate;
+    }
+
+    @Override
+    public Integer getBcdLocal() {
+        return bcdLocal;
+    }
+
+    @Override
+    public String toString() {
+        return "DefaultSubscriptionBillingEvent{" +
+               "type=" + type +
+               ", planName='" + planName + '\'' +
+               ", planPhaseName='" + planPhaseName + '\'' +
+               ", effectiveDate=" + effectiveDate +
+               ", totalOrdering=" + totalOrdering +
+               ", lastChangePlanDate=" + lastChangePlanDate +
+               ", bcdLocal=" + bcdLocal +
+               '}';
+    }
+}