killbill-uncached

Fixes #193 Squashed commit of the following: commit f4778b1e938472b785f6269cc28a92a92ce9aa6f Author:

10/22/2014 8:16:49 PM

Changes

pom.xml 2(+1 -1)

Details

diff --git a/api/src/main/java/org/killbill/billing/junction/BillingInternalApi.java b/api/src/main/java/org/killbill/billing/junction/BillingInternalApi.java
index f8cda97..0f9e8fa 100644
--- a/api/src/main/java/org/killbill/billing/junction/BillingInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/junction/BillingInternalApi.java
@@ -18,15 +18,13 @@ package org.killbill.billing.junction;
 
 import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.joda.time.LocalDate;
 import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.invoice.api.DryRunArguments;
 
 public interface BillingInternalApi {
 
     /**
      * @return an ordered list of billing event for the given accounts
      */
-    public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(UUID accountId, InternalCallContext context);
+    public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(UUID accountId, DryRunArguments dryRunArguments, InternalCallContext context);
 }
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 4522bf6..245f81a 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
@@ -29,53 +29,54 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.entitlement.api.EntitlementAOStatusDryRun;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
+import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.util.entity.Pagination;
 
 public interface SubscriptionBaseInternalApi {
 
-    public SubscriptionBase createSubscription(final UUID bundleId, final PlanPhaseSpecifier spec, final DateTime requestedDateWithMs,
-                                               final InternalCallContext context) throws SubscriptionBaseApiException;
+    public SubscriptionBase createSubscription(UUID bundleId, PlanPhaseSpecifier spec, DateTime requestedDateWithMs,
+                                               InternalCallContext context) throws SubscriptionBaseApiException;
 
-    public SubscriptionBaseBundle createBundleForAccount(final UUID accountId, final String bundleName, final InternalCallContext context)
+    public SubscriptionBaseBundle createBundleForAccount(UUID accountId, String bundleName, InternalCallContext context)
             throws SubscriptionBaseApiException;
 
-    public List<SubscriptionBaseBundle> getBundlesForAccountAndKey(final UUID accountId, final String bundleKey, final InternalTenantContext context)
+    public List<SubscriptionBaseBundle> getBundlesForAccountAndKey(UUID accountId, String bundleKey, InternalTenantContext context)
             throws SubscriptionBaseApiException;
 
-    public List<SubscriptionBaseBundle> getBundlesForAccount(final UUID accountId, final InternalTenantContext context);
+    public List<SubscriptionBaseBundle> getBundlesForAccount(UUID accountId, InternalTenantContext context);
 
-    public List<SubscriptionBaseBundle> getBundlesForKey(final String bundleKey, final InternalTenantContext context);
+    public List<SubscriptionBaseBundle> getBundlesForKey(String bundleKey, InternalTenantContext context);
 
-    public Pagination<SubscriptionBaseBundle> getBundles(final Long offset, final Long limit, final InternalTenantContext context);
+    public Pagination<SubscriptionBaseBundle> getBundles(Long offset, Long limit, InternalTenantContext context);
 
-    public Pagination<SubscriptionBaseBundle> searchBundles(final String searchKey, final Long offset, final Long limit, final InternalTenantContext context);
+    public Pagination<SubscriptionBaseBundle> searchBundles(String searchKey, Long offset, Long limit, InternalTenantContext context);
 
-    public Iterable<UUID> getNonAOSubscriptionIdsForKey(final String bundleKey, final InternalTenantContext context);
+    public Iterable<UUID> getNonAOSubscriptionIdsForKey(String bundleKey, InternalTenantContext context);
 
-    public List<SubscriptionBase> getSubscriptionsForBundle(final UUID bundleId, final InternalTenantContext context);
+    public List<SubscriptionBase> getSubscriptionsForBundle(UUID bundleId, DryRunArguments dryRunArguments, InternalTenantContext context)
+            throws SubscriptionBaseApiException;
 
-    public Map<UUID, List<SubscriptionBase>> getSubscriptionsForAccount(final InternalTenantContext context);
+    public Map<UUID, List<SubscriptionBase>> getSubscriptionsForAccount(InternalTenantContext context);
 
-    public SubscriptionBase getBaseSubscription(final UUID bundleId, final InternalTenantContext context) throws SubscriptionBaseApiException;
+    public SubscriptionBase getBaseSubscription(UUID bundleId, InternalTenantContext context) throws SubscriptionBaseApiException;
 
-    public SubscriptionBase getSubscriptionFromId(final UUID id, final InternalTenantContext context) throws SubscriptionBaseApiException;
+    public SubscriptionBase getSubscriptionFromId(UUID id, InternalTenantContext context) throws SubscriptionBaseApiException;
 
-    public SubscriptionBaseBundle getBundleFromId(final UUID id, final InternalTenantContext context) throws SubscriptionBaseApiException;
+    public SubscriptionBaseBundle getBundleFromId(UUID id, InternalTenantContext context) throws SubscriptionBaseApiException;
 
-    public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId, final InternalTenantContext context) throws SubscriptionBaseApiException;
+    public UUID getAccountIdFromSubscriptionId(UUID subscriptionId, InternalTenantContext context) throws SubscriptionBaseApiException;
 
-    public void setChargedThroughDate(final UUID subscriptionId, final DateTime chargedThruDate, final InternalCallContext context);
+    public void setChargedThroughDate(UUID subscriptionId, DateTime chargedThruDate, InternalCallContext context);
 
-    public List<EffectiveSubscriptionInternalEvent> getAllTransitions(final SubscriptionBase subscription, final InternalTenantContext context);
+    public List<EffectiveSubscriptionInternalEvent> getAllTransitions(SubscriptionBase subscription, InternalTenantContext context);
 
-    public List<EffectiveSubscriptionInternalEvent> getBillingTransitions(final SubscriptionBase subscription, final InternalTenantContext context);
+    public List<EffectiveSubscriptionInternalEvent> getBillingTransitions(SubscriptionBase subscription, InternalTenantContext context);
 
-    public DateTime getNextBillingDate(final UUID accountId, final InternalTenantContext context);
+    public List<EntitlementAOStatusDryRun> getDryRunChangePlanStatus(UUID subscriptionId, @Nullable String baseProductName,
+                                                                     DateTime requestedDate, InternalTenantContext context) throws SubscriptionBaseApiException;
 
-    public List<EntitlementAOStatusDryRun> getDryRunChangePlanStatus(final UUID subscriptionId, @Nullable final String baseProductName,
-                                                                     final DateTime requestedDate, final InternalTenantContext context) throws SubscriptionBaseApiException;
+    public void updateExternalKey(UUID bundleId, String newExternalKey, InternalCallContext context);
 
-    public void updateExternalKey(final UUID bundleId, final String newExternalKey, final InternalCallContext context);
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
index 11e9f1b..9982a0a 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
@@ -17,6 +17,7 @@
 package org.killbill.billing.beatrix.integration;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
@@ -28,6 +29,7 @@ import org.killbill.billing.account.api.AccountData;
 import org.killbill.billing.api.TestApiListener.NextEvent;
 import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
 import org.killbill.billing.beatrix.util.PaymentChecker.ExpectedPaymentCheck;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.PriceListSet;
@@ -37,7 +39,9 @@ import org.killbill.billing.entitlement.api.Entitlement;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.entitlement.api.SubscriptionBundle;
+import org.killbill.billing.entitlement.api.SubscriptionEventType;
 import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
@@ -62,37 +66,60 @@ public class TestIntegration extends TestIntegrationBase {
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDay(new LocalDate(2012, 4, 1));
 
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+
         //
         // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
         //
+
+        TestDryRunArguments dryRun = new TestDryRunArguments("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null,
+                                                             SubscriptionEventType.START_BILLING, null, null, clock.getUTCNow(), null);
+        Invoice dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
+
         final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
         // Check bundle after BP got created otherwise we get an error from auditApi.
         subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
-        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, expectedInvoices);
+        expectedInvoices.clear();
+
         //
         // ADD ADD_ON ON THE SAME DAY
         //
+        dryRun = new TestDryRunArguments("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, null, null,
+                                         SubscriptionEventType.START_BILLING, null, bpSubscription.getBundleId(), clock.getUTCNow(), null);
+        dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("399.95")));
+        invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
+
         addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT);
 
-        Invoice invoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("399.95")));
+        Invoice invoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
         paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 4, 1), new BigDecimal("399.95"), TransactionStatus.SUCCESS, invoice.getId(), Currency.USD));
+        expectedInvoices.clear();
 
         //
         // CANCEL BP ON THE SAME DAY (we should have two cancellations, BP and AO)
         // There is no invoice created as we only adjust the previous invoice.
         //
+        dryRun = new TestDryRunArguments(null, null, null, null, null, SubscriptionEventType.STOP_BILLING, bpSubscription.getId(),
+                                         bpSubscription.getBundleId(), clock.getUTCNow(), null);
+        dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-399.95")));
+        // The second invoice should be adjusted for the AO (we paid for the full period) and since we paid we should also see a CBA
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 4, 1), InvoiceItemType.CBA_ADJ, new BigDecimal("399.95"),
+                                                          false /* Avoid checking dates for CBA because code is using context and context createdDate is wrong  in the test as we reset the clock too late, bummer... */ ));
+        invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
+
+
         cancelEntitlementAndCheckForCompletion(bpSubscription, clock.getUTCNow(), NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.CANCEL, NextEvent.INVOICE);
-        invoiceChecker.checkInvoice(account.getId(), 2,
-                                    callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("399.95")));
 
         invoiceChecker.checkInvoice(account.getId(), 3,
                                     callContext,
-                                    // The second invoice should be adjusted for the AO (we paid for the full period) and since we paid we should also see a CBA
-                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-399.95")),
-                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 4, 1), InvoiceItemType.CBA_ADJ, new BigDecimal("399.95")));
+                                    expectedInvoices);
 
         checkNoMoreInvoiceToGenerate(account);
-
     }
 
     @Test(groups = "slow")
@@ -108,6 +135,8 @@ public class TestIntegration extends TestIntegrationBase {
         clock.setTime(initialCreationDate);
         int invoiceItemCount = 1;
 
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+
         //
         // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
         //
@@ -120,9 +149,17 @@ public class TestIntegration extends TestIntegrationBase {
         //
         // CHANGE PLAN IMMEDIATELY AND EXPECT BOTH EVENTS: NextEvent.CHANGE NextEvent.INVOICE
         //
+        TestDryRunArguments dryRun = new TestDryRunArguments("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null, SubscriptionEventType.CHANGE,
+                                                             subscription.getId(), subscription.getBundleId(), clock.getUTCNow(), null);
+        Invoice dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
+
+
         changeEntitlementAndCheckForCompletion(baseEntitlement, "Assault-Rifle", BillingPeriod.MONTHLY, null, NextEvent.CHANGE, NextEvent.INVOICE);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, expectedInvoices);
         invoiceChecker.checkChargedThroughDate(subscription.getId(), clock.getUTCToday(), callContext);
+        expectedInvoices.clear();
 
         //
         // MOVE 4 * TIME THE CLOCK
@@ -130,10 +167,19 @@ public class TestIntegration extends TestIntegrationBase {
         setDateAndCheckForCompletion(new DateTime(2012, 2, 28, 23, 59, 59, 0, testTimeZone));
         setDateAndCheckForCompletion(new DateTime(2012, 2, 29, 23, 59, 59, 0, testTimeZone));
         setDateAndCheckForCompletion(new DateTime(2012, 3, 1, 23, 59, 59, 0, testTimeZone));
-        setDateAndCheckForCompletion(new DateTime(2012, 3, 2, 23, 59, 59, 0, testTimeZone), NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 2),
-                                                                                                                   new LocalDate(2012, 3, 31), InvoiceItemType.RECURRING, new BigDecimal("561.24")));
+
+        DateTime nextDate = clock.getUTCNow().plusDays(1);
+        dryRun = new TestDryRunArguments();
+        dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), new LocalDate(nextDate, testTimeZone), dryRun, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 2), new LocalDate(2012, 3, 31), InvoiceItemType.RECURRING, new BigDecimal("561.24")));
+
+        invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
+
+
+        setDateAndCheckForCompletion(nextDate, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, expectedInvoices);
         invoiceChecker.checkChargedThroughDate(subscription.getId(), new LocalDate(2012, 3, 31), callContext);
+        expectedInvoices.clear();
 
         //
         // CHANGE PLAN EOT AND EXPECT NOTHING
@@ -146,9 +192,19 @@ public class TestIntegration extends TestIntegrationBase {
         //
         final LocalDate firstRecurringPistolDate = subscription.getChargedThroughDate().toLocalDate();
         final LocalDate secondRecurringPistolDate = firstRecurringPistolDate.plusMonths(1);
+
+
+        nextDate = clock.getUTCNow().plusDays(31);
+        dryRun = new TestDryRunArguments();
+        dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), new LocalDate(nextDate, testTimeZone), dryRun, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 31), new LocalDate(2012, 4, 30), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+
+        invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
+
         addDaysAndCheckForCompletion(31, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT);
-        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 31), new LocalDate(2012, 4, 30), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, expectedInvoices);
         invoiceChecker.checkChargedThroughDate(subscription.getId(), secondRecurringPistolDate, callContext);
+        expectedInvoices.clear();
 
         //
         // MOVE 3 * TIME AFTER NEXT BILL CYCLE DAY AND EXPECT EVENT : NextEvent.INVOICE, NextEvent.PAYMENT
@@ -222,6 +278,19 @@ public class TestIntegration extends TestIntegrationBase {
         //
         // CHANGE PLAN EOT AND EXPECT NOTHING
         //
+
+
+        TestDryRunArguments dryRun = new TestDryRunArguments("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null, SubscriptionEventType.CHANGE,
+                                                             subscription.getId(), subscription.getBundleId(), null, null);
+        try {
+           invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
+            Assert.fail("Call should return no invoices");
+        } catch (InvoiceApiException e) {
+            System.out.print("foo");
+
+        }
+
+
         baseEntitlement = changeEntitlementAndCheckForCompletion(baseEntitlement, "Pistol", BillingPeriod.MONTHLY, null);
         subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index 02c99b0..cdd9e5a 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -49,6 +49,7 @@ import org.killbill.billing.beatrix.util.SubscriptionChecker;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
@@ -57,9 +58,10 @@ import org.killbill.billing.entitlement.api.Entitlement;
 import org.killbill.billing.entitlement.api.EntitlementApi;
 import org.killbill.billing.entitlement.api.EntitlementApiException;
 import org.killbill.billing.entitlement.api.SubscriptionApi;
+import org.killbill.billing.entitlement.api.SubscriptionEventType;
+import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
-import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoiceService;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
@@ -296,7 +298,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
 
     protected void checkNoMoreInvoiceToGenerate(final Account account) {
         try {
-            invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), false, callContext);
+            invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), null, callContext);
             fail("Should not have generated an extra invoice");
         } catch (final InvoiceApiException e) {
             assertEquals(e.getCode(), ErrorCode.INVOICE_NOTHING_TO_DO.getCode());
@@ -407,7 +409,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
         }, events);
     }
 
-    protected Payment createPaymentAndCheckForCompletion(final Account account, final Invoice invoice, final BigDecimal amount, final Currency currency,  final NextEvent... events) {
+    protected Payment createPaymentAndCheckForCompletion(final Account account, final Invoice invoice, final BigDecimal amount, final Currency currency, final NextEvent... events) {
         return doCallAndCheckForCompletion(new Function<Void, Payment>() {
             @Override
             public Payment apply(@Nullable final Void input) {
@@ -435,7 +437,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
                     final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false);
                     properties.add(prop1);
 
-                    return paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, invoice.getBalance(), invoice.getCurrency(),  UUID.randomUUID().toString(),
+                    return paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, invoice.getBalance(), invoice.getCurrency(), UUID.randomUUID().toString(),
                                                                        UUID.randomUUID().toString(), properties, PAYMENT_OPTIONS, callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
@@ -682,8 +684,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
         assertEquals(tags.size(), 1);
     }
 
-
-    protected void remove_AUTO_PAY_OFF_Tag(final UUID id, final ObjectType type, final NextEvent...additionalEvents) throws TagDefinitionApiException, TagApiException {
+    protected void remove_AUTO_PAY_OFF_Tag(final UUID id, final ObjectType type, final NextEvent... additionalEvents) throws TagDefinitionApiException, TagApiException {
         busHandler.pushExpectedEvent(NextEvent.TAG);
         busHandler.pushExpectedEvents(additionalEvents);
         tagUserApi.removeTag(id, type, ControlTagType.AUTO_PAY_OFF.getId(), callContext);
@@ -702,4 +703,71 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
         log.debug("            ************    DONE WITH BUS HANDLER CHECK    ********************");
         return result;
     }
+
+    protected static class TestDryRunArguments implements DryRunArguments {
+
+        private final PlanPhaseSpecifier spec;
+        private final SubscriptionEventType action;
+        private final UUID subscriptionId;
+        private final UUID bundleId;
+        private final DateTime effectiveDate;
+        private final BillingActionPolicy billingPolicy;
+
+        public TestDryRunArguments() {
+            this.spec = null;
+            this.action = null;
+            this.subscriptionId = null;
+            this.bundleId = null;
+            this.effectiveDate = null;
+            this.billingPolicy = null;
+        }
+
+        public TestDryRunArguments(final String productName,
+                                   final ProductCategory category,
+                                   final BillingPeriod billingPeriod,
+                                   final String priceList,
+                                   final PhaseType phaseType,
+                                   final SubscriptionEventType action,
+                                   final UUID subscriptionId,
+                                   final UUID bundleId,
+                                   final DateTime effectiveDate,
+                                   final BillingActionPolicy billingPolicy) {
+            this.spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceList, phaseType);
+            this.action = action;
+            this.subscriptionId = subscriptionId;
+            this.bundleId = bundleId;
+            this.effectiveDate = effectiveDate;
+            this.billingPolicy = billingPolicy;
+        }
+
+        @Override
+        public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+            return spec;
+        }
+
+        @Override
+        public SubscriptionEventType getAction() {
+            return action;
+        }
+
+        @Override
+        public UUID getSubscriptionId() {
+            return subscriptionId;
+        }
+
+        @Override
+        public DateTime getEffectiveDate() {
+            return effectiveDate;
+        }
+
+        @Override
+        public UUID getBundleId() {
+            return bundleId;
+        }
+
+        @Override
+        public BillingActionPolicy getBillingActionPolicy() {
+            return billingPolicy;
+        }
+    }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
index 8c73b48..3dc6b0e 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
@@ -18,10 +18,12 @@
 package org.killbill.billing.beatrix.integration;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.List;
 
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
+import org.killbill.billing.entitlement.api.SubscriptionEventType;
 import org.testng.annotations.Test;
 
 import org.killbill.billing.account.api.Account;
@@ -76,21 +78,24 @@ public class TestSubscription extends TestIntegrationBase {
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
         invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
 
+
         //
         // FORCE AN IMMEDIATE CHANGE OF THE BILLING PERIOD
         //
+        toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("169.32")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2013, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-2334.20")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 5, 11), InvoiceItemType.CBA_ADJ, new BigDecimal("2164.88"), false /* Issue with test where created date for context is wrong*/));
+
+        TestDryRunArguments dryRun = new TestDryRunArguments(productName, ProductCategory.BASE, BillingPeriod.MONTHLY, null, null,
+                                                             SubscriptionEventType.CHANGE, bpEntitlement.getId(), bpEntitlement.getBundleId(), null, BillingActionPolicy.IMMEDIATE);
+        Invoice dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
+        invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, toBeChecked);
+
         changeEntitlementAndCheckForCompletion(bpEntitlement, productName, BillingPeriod.MONTHLY, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE);
 
         invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
         assertEquals(invoices.size(), 3);
-        toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
-                new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
-        invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
-
-        toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
-                new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("169.32")),
-                new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2013, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-2334.20")),
-                new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 11), new LocalDate(2012, 5, 11), InvoiceItemType.CBA_ADJ, new BigDecimal("2164.88")));
         invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
 
         //
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
index 5fe963b..d3f24c2 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
@@ -20,16 +20,7 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
-import javax.annotation.Nullable;
-
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
-import org.joda.time.LocalTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-
 import org.killbill.billing.entitlement.api.DefaultEntitlement;
 import org.killbill.billing.entitlement.api.EntitlementApi;
 import org.killbill.billing.entitlement.api.EntitlementApiException;
@@ -40,13 +31,15 @@ import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.util.callcontext.CallContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 
-import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
@@ -92,6 +85,10 @@ public class InvoiceChecker {
     public void checkInvoice(final UUID invoiceId, final CallContext context, final List<ExpectedInvoiceItemCheck> expected) throws InvoiceApiException {
         final Invoice invoice = invoiceUserApi.getInvoice(invoiceId, context);
         Assert.assertNotNull(invoice);
+        checkInvoice(invoice, context, expected);
+    }
+
+    public void checkInvoiceNoAudits(final Invoice invoice, final CallContext context, final List<ExpectedInvoiceItemCheck> expected) throws InvoiceApiException {
 
         final List<InvoiceItem> actual = invoice.getInvoiceItems();
         Assert.assertEquals(actual.size(), expected.size());
@@ -156,6 +153,11 @@ public class InvoiceChecker {
                 Assert.fail(failureMessage);
             }
         }
+
+    }
+
+    public void checkInvoice(final Invoice invoice, final CallContext context, final List<ExpectedInvoiceItemCheck> expected) throws InvoiceApiException {
+        checkInvoiceNoAudits(invoice, context, expected);
         auditChecker.checkInvoiceCreated(invoice, context);
     }
 
@@ -191,23 +193,32 @@ public class InvoiceChecker {
         private final LocalDate startDate;
         private final LocalDate endDate;
         private final InvoiceItemType type;
-        private final BigDecimal Amount;
+        private final BigDecimal amount;
 
-        public ExpectedInvoiceItemCheck(final InvoiceItemType type, final BigDecimal amount) {
-            this.checkDates = false;
+        public ExpectedInvoiceItemCheck(final InvoiceItemType type, final BigDecimal amount, boolean checkDates) {
+            this.checkDates = checkDates;
             this.type = type;
             this.startDate = null;
             this.endDate = null;
-            Amount = amount;
+            this.amount = amount;
+        }
+
+        public ExpectedInvoiceItemCheck(final InvoiceItemType type, final BigDecimal amount) {
+            this(type, amount, false);
         }
 
         public ExpectedInvoiceItemCheck(final LocalDate startDate, final LocalDate endDate,
-                                        final InvoiceItemType type, final BigDecimal amount) {
-            this.checkDates = true;
+                                        final InvoiceItemType type, final BigDecimal amount, boolean checkDates) {
+            this.checkDates = checkDates;
             this.startDate = startDate;
             this.endDate = endDate;
             this.type = type;
-            Amount = amount;
+            this.amount = amount;
+        }
+
+        public ExpectedInvoiceItemCheck(final LocalDate startDate, final LocalDate endDate,
+                                        final InvoiceItemType type, final BigDecimal amount) {
+            this(startDate, endDate, type, amount, true);
         }
 
         public boolean shouldCheckDates() {
@@ -227,7 +238,7 @@ public class InvoiceChecker {
         }
 
         public BigDecimal getAmount() {
-            return Amount;
+            return amount;
         }
     }
 
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
index 3475b66..d74f611 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
@@ -360,7 +360,7 @@ public class DefaultEntitlementApi implements EntitlementApi {
 
             // Block all associated subscriptions - TODO Do we want to block the bundle as well (this will add an extra STOP_ENTITLEMENT event in the bundle timeline stream)?
             // Note that there is no un-transfer at the moment, so we effectively add a blocking state on disk for all subscriptions
-            for (final SubscriptionBase subscriptionBase : subscriptionBaseInternalApi.getSubscriptionsForBundle(baseBundle.getId(), contextWithValidAccountRecordId)) {
+            for (final SubscriptionBase subscriptionBase : subscriptionBaseInternalApi.getSubscriptionsForBundle(baseBundle.getId(), null, contextWithValidAccountRecordId)) {
                 final BlockingState blockingState = new DefaultBlockingState(subscriptionBase.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, requestedDate);
                 entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingState, contextWithValidAccountRecordId);
             }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
index d7246fc..835fd6c 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
@@ -224,7 +224,7 @@ public class EventsStreamBuilder {
         try {
             subscription = subscriptionInternalApi.getSubscriptionFromId(entitlementId, internalTenantContext);
             bundle = subscriptionInternalApi.getBundleFromId(subscription.getBundleId(), internalTenantContext);
-            allSubscriptionsForBundle = subscriptionInternalApi.getSubscriptionsForBundle(subscription.getBundleId(), internalTenantContext);
+            allSubscriptionsForBundle = subscriptionInternalApi.getSubscriptionsForBundle(subscription.getBundleId(), null, internalTenantContext);
             baseSubscription = Iterables.<SubscriptionBase>tryFind(allSubscriptionsForBundle,
                                                                    new Predicate<SubscriptionBase>() {
                                                                        @Override
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 34474c8..a8b7364 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -34,6 +34,7 @@ import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.InvoiceDispatcher;
+import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
@@ -199,8 +200,9 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
                                                                     }));
     }
 
+
     @Override
-    public Invoice triggerInvoiceGeneration(final UUID accountId, final LocalDate targetDate, final boolean dryRun,
+    public Invoice triggerInvoiceGeneration(final UUID accountId, final LocalDate targetDate, final DryRunArguments dryRunArguments,
                                             final CallContext context) throws InvoiceApiException {
         final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(accountId, context);
 
@@ -212,7 +214,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
         }
 
         final DateTime processingDateTime = targetDate.toDateTimeAtCurrentTime(account.getTimeZone());
-        final Invoice result = dispatcher.processAccount(accountId, processingDateTime, dryRun, internalContext);
+        final Invoice result = dispatcher.processAccount(accountId, processingDateTime, dryRunArguments, internalContext);
         if (result == null) {
             throw new InvoiceApiException(ErrorCode.INVOICE_NOTHING_TO_DO, accountId, targetDate);
         } else {
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 edc9b8a..6e2c67a 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -40,12 +40,15 @@ import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingMode;
+import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.Usage;
+import org.killbill.billing.entitlement.api.SubscriptionEventType;
 import org.killbill.billing.events.BusInternalEvent;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
 import org.killbill.billing.events.InvoiceAdjustmentInternalEvent;
 import org.killbill.billing.events.InvoiceInternalEvent;
+import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
@@ -149,7 +152,7 @@ public class InvoiceDispatcher {
                 return;
             }
             final UUID accountId = subscriptionApi.getAccountIdFromSubscriptionId(subscriptionId, context);
-            processAccount(accountId, targetDate, false, context);
+            processAccount(accountId, targetDate, null, context);
         } catch (final SubscriptionBaseApiException e) {
             log.error("Failed handling SubscriptionBase change.",
                       new InvoiceApiException(ErrorCode.INVOICE_NO_ACCOUNT_ID_FOR_SUBSCRIPTION_ID, subscriptionId.toString()));
@@ -157,12 +160,12 @@ public class InvoiceDispatcher {
     }
 
     public Invoice processAccount(final UUID accountId, final DateTime targetDate,
-                                  final boolean dryRun, final InternalCallContext context) throws InvoiceApiException {
+                                  @Nullable final DryRunArguments dryRunArguments, final InternalCallContext context) throws InvoiceApiException {
         GlobalLock lock = null;
         try {
             lock = locker.lockWithNumberOfTries(LockerType.ACCOUNT_FOR_INVOICE_PAYMENTS.toString(), accountId.toString(), NB_LOCK_TRY);
 
-            return processAccountWithLock(accountId, targetDate, dryRun, context);
+            return processAccountWithLock(accountId, targetDate, dryRunArguments, context);
         } catch (final LockFailedException e) {
             // Not good!
             log.error(String.format("Failed to process invoice for account %s, targetDate %s",
@@ -176,11 +179,13 @@ public class InvoiceDispatcher {
     }
 
     private Invoice processAccountWithLock(final UUID accountId, final DateTime targetDateTime,
-                                           final boolean dryRun, final InternalCallContext context) throws InvoiceApiException {
+                                           @Nullable final DryRunArguments dryRunArguments, final InternalCallContext context) throws InvoiceApiException {
+
+        final boolean isDryRun = dryRunArguments != null;
         try {
 
             // Make sure to first set the BCD if needed then get the account object (to have the BCD set)
-            final BillingEventSet billingEvents = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId, context);
+            final BillingEventSet billingEvents = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId, dryRunArguments, context);
 
             final Account account = accountApi.getAccountById(accountId, context);
             final DateAndTimeZoneContext dateAndTimeZoneContext = billingEvents.iterator().hasNext() ?
@@ -207,7 +212,7 @@ public class InvoiceDispatcher {
             //
             if (invoice == null) {
                 log.info("Generated null invoice for accountId {} and targetDate {} (targetDateTime {})", new Object[]{accountId, targetDate, targetDateTime});
-                if (!dryRun) {
+                if (!isDryRun) {
                     final BusInternalEvent event = new DefaultNullInvoiceEvent(accountId, clock.getUTCToday(),
                                                                                context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
                     postEvent(event, accountId, context);
@@ -240,7 +245,7 @@ public class InvoiceDispatcher {
             }
 
             boolean isRealInvoiceWithItems = false;
-            if (!dryRun) {
+            if (!isDryRun) {
 
                 // Extract the set of invoiceId for which we see items that don't belong to current generated invoice
                 final Set<UUID> adjustedUniqueOtherInvoiceId = new TreeSet<UUID>();
@@ -296,7 +301,7 @@ public class InvoiceDispatcher {
                 }
             }
 
-            if (account.isNotifiedForInvoices() && isRealInvoiceWithItems && !dryRun) {
+            if (account.isNotifiedForInvoices() && isRealInvoiceWithItems && !isDryRun) {
                 // Need to re-hydrate the invoice object to get the invoice number (record id)
                 // API_FIX InvoiceNotifier public API?
                 invoiceNotifier.notify(account, new DefaultInvoice(invoiceDao.getById(invoice.getId(), context)), buildTenantContext(context));
@@ -443,5 +448,4 @@ public class InvoiceDispatcher {
             }
         }
     }
-
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java
index ed949df..74ef87f 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java
@@ -65,7 +65,7 @@ public class InvoiceListener {
 
         try {
             final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "RepairBundle", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
-            dispatcher.processAccount(event.getAccountId(), event.getEffectiveDate(), false, context);
+            dispatcher.processAccount(event.getAccountId(), event.getEffectiveDate(), null, context);
         } catch (InvoiceApiException e) {
             log.error(e.getMessage());
         }
@@ -94,7 +94,7 @@ public class InvoiceListener {
 
         try {
             final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "SubscriptionBaseTransition", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
-            dispatcher.processAccount(event.getAccountId(), event.getEffectiveTransitionTime(), false, context);
+            dispatcher.processAccount(event.getAccountId(), event.getEffectiveTransitionTime(), null, context);
         } catch (InvoiceApiException e) {
             log.error(e.getMessage());
         }
@@ -111,7 +111,7 @@ public class InvoiceListener {
         try {
             final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "SubscriptionBaseTransition", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
             final UUID accountId = accountApi.getByRecordId(event.getSearchKey1(), context);
-            dispatcher.processAccount(accountId, clock.getUTCNow(), false, context);
+            dispatcher.processAccount(accountId, clock.getUTCNow(), null, context);
         } catch (InvoiceApiException e) {
             log.error(e.getMessage());
         } catch (AccountApiException e) {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java
index 7d0d5c6..e293356 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java
@@ -63,7 +63,7 @@ public class InvoiceTagHandler {
 
     private void processUnpaid_AUTO_INVOICING_OFF_invoices(final UUID accountId, final InternalCallContext context) {
         try {
-            dispatcher.processAccount(accountId, clock.getUTCNow(), false, context);
+            dispatcher.processAccount(accountId, clock.getUTCNow(), null, context);
         } catch (InvoiceApiException e) {
             log.warn(String.format("Failed to process process removal AUTO_INVOICING_OFF for account %s", accountId), e);
         }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
index cf40b6d..c184a59 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
@@ -38,6 +38,9 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
+import org.killbill.billing.entitlement.api.SubscriptionEventType;
+import org.killbill.billing.invoice.TestInvoiceHelper.DryRunFutureDateArguments;
+import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
@@ -51,6 +54,7 @@ import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
 import org.killbill.clock.ClockMock;
+import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
@@ -85,7 +89,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
                                                       fixedPrice, BigDecimal.ONE, currency, BillingPeriod.MONTHLY, 1,
                                                       BillingMode.IN_ADVANCE, "", 1L, SubscriptionBaseTransitionType.CREATE));
 
-        Mockito.when(billingApi.getBillingEventsForAccountAndUpdateAccountBCD(Mockito.<UUID>any(), Mockito.<InternalCallContext>any())).thenReturn(events);
+        Mockito.when(billingApi.getBillingEventsForAccountAndUpdateAccountBCD(Mockito.<UUID>any(), Mockito.<DryRunArguments>any(),  Mockito.<InternalCallContext>any())).thenReturn(events);
 
         final DateTime target = new DateTime();
 
@@ -94,21 +98,21 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
                                                                    nonEntityDao, invoiceNotifier, locker, busService.getBus(),
                                                                    clock, controllerDispatcher);
 
-        Invoice invoice = dispatcher.processAccount(accountId, target, true, context);
+        Invoice invoice = dispatcher.processAccount(accountId, target, new DryRunFutureDateArguments(), context);
         Assert.assertNotNull(invoice);
 
         List<InvoiceModelDao> invoices = invoiceDao.getInvoicesByAccount(context);
         Assert.assertEquals(invoices.size(), 0);
 
         // Try it again to double check
-        invoice = dispatcher.processAccount(accountId, target, true, context);
+        invoice = dispatcher.processAccount(accountId, target, new DryRunFutureDateArguments(), context);
         Assert.assertNotNull(invoice);
 
         invoices = invoiceDao.getInvoicesByAccount(context);
         Assert.assertEquals(invoices.size(), 0);
 
         // This time no dry run
-        invoice = dispatcher.processAccount(accountId, target, false, context);
+        invoice = dispatcher.processAccount(accountId, target, null, context);
         Assert.assertNotNull(invoice);
 
         invoices = invoiceDao.getInvoicesByAccount(context);
@@ -141,13 +145,13 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
                                                       new MockPlanPhase(jetTrialEvergreen1000USD, PhaseType.EVERGREEN), null, new BigDecimal("1000"), account.getCurrency(), BillingPeriod.MONTHLY,
                                                       31, BillingMode.IN_ADVANCE, "CHANGE", 3L, SubscriptionBaseTransitionType.CHANGE));
 
-        Mockito.when(billingApi.getBillingEventsForAccountAndUpdateAccountBCD(Mockito.<UUID>any(), Mockito.<InternalCallContext>any())).thenReturn(events);
+        Mockito.when(billingApi.getBillingEventsForAccountAndUpdateAccountBCD(Mockito.<UUID>any(), Mockito.<DryRunArguments>any(), Mockito.<InternalCallContext>any())).thenReturn(events);
         final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
         final InvoiceDispatcher dispatcher = new InvoiceDispatcher(pluginRegistry, generator, accountApi, billingApi, subscriptionApi, invoiceDao,
                                                                    nonEntityDao, invoiceNotifier, locker, busService.getBus(),
                                                                    clock, controllerDispatcher);
 
-        final Invoice invoice = dispatcher.processAccount(account.getId(), new DateTime("2012-07-30T00:00:00.000Z"), false, context);
+        final Invoice invoice = dispatcher.processAccount(account.getId(), new DateTime("2012-07-30T00:00:00.000Z"), null, context);
         Assert.assertNotNull(invoice);
 
         final List<InvoiceItem> invoiceItems = invoice.getInvoiceItems();
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 955e9a0..407e59f 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
@@ -38,13 +38,17 @@ import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.MockPlan;
 import org.killbill.billing.catalog.MockPlanPhase;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingMode;
 import org.killbill.billing.catalog.api.BillingPeriod;
 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.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.Usage;
+import org.killbill.billing.entitlement.api.SubscriptionEventType;
 import org.killbill.billing.entity.EntityPersistenceException;
+import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
@@ -194,14 +198,14 @@ public class TestInvoiceHelper {
                                           fixedPrice, BigDecimal.ONE, currency, BillingPeriod.MONTHLY, 1,
                                           BillingMode.IN_ADVANCE, "", 1L, SubscriptionBaseTransitionType.CREATE));
 
-        Mockito.when(billingApi.getBillingEventsForAccountAndUpdateAccountBCD(Mockito.<UUID>any(), Mockito.<InternalCallContext>any())).thenReturn(events);
+        Mockito.when(billingApi.getBillingEventsForAccountAndUpdateAccountBCD(Mockito.<UUID>any(), Mockito.<DryRunArguments>any(), Mockito.<InternalCallContext>any())).thenReturn(events);
 
         final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
         final InvoiceDispatcher dispatcher = new InvoiceDispatcher(pluginRegistry, generator, accountApi, billingApi, subscriptionApi,
                                                                    invoiceDao, nonEntityDao, invoiceNotifier, locker, busService.getBus(),
                                                                    clock, cacheControllerDispatcher);
 
-        Invoice invoice = dispatcher.processAccount(account.getId(), targetDate, true, internalCallContext);
+        Invoice invoice = dispatcher.processAccount(account.getId(), targetDate, new DryRunFutureDateArguments(), internalCallContext);
         Assert.assertNotNull(invoice);
 
         final InternalCallContext context = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
@@ -209,7 +213,7 @@ public class TestInvoiceHelper {
         List<InvoiceModelDao> invoices = invoiceDao.getInvoicesByAccount(context);
         Assert.assertEquals(invoices.size(), 0);
 
-        invoice = dispatcher.processAccount(account.getId(), targetDate, false, context);
+        invoice = dispatcher.processAccount(account.getId(), targetDate, null, context);
         Assert.assertNotNull(invoice);
 
         invoices = invoiceDao.getInvoicesByAccount(context);
@@ -420,4 +424,34 @@ public class TestInvoiceHelper {
             }
         };
     }
+    public static class DryRunFutureDateArguments implements DryRunArguments {
+        public DryRunFutureDateArguments() {
+        }
+        @Override
+        public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+            return null;
+        }
+        @Override
+        public SubscriptionEventType getAction() {
+            return null;
+        }
+        @Override
+        public UUID getSubscriptionId() {
+            return null;
+        }
+        @Override
+        public DateTime getEffectiveDate() {
+            return null;
+        }
+
+        @Override
+        public UUID getBundleId() {
+            return null;
+        }
+
+        @Override
+        public BillingActionPolicy getBillingActionPolicy() {
+            return null;
+        }
+    }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceDryRunJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceDryRunJson.java
new file mode 100644
index 0000000..3dbd829
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceDryRunJson.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 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.jaxrs.json;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.LocalDate;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class InvoiceDryRunJson {
+
+    private final String dryRunAction;
+    private final String phaseType;
+    private final String productName;
+    private final String productCategory;
+    private final String billingPeriod;
+    private final String priceListName;
+    private final LocalDate effectiveDate;
+    private final String subscriptionId;
+    private final String bundleId;
+    private final String billingPolicy;
+
+    @JsonCreator
+    public InvoiceDryRunJson(@JsonProperty("dryRunAction") @Nullable final String dryRunAction,
+                             @JsonProperty("phaseType") @Nullable final String phaseType,
+                             @JsonProperty("productName") @Nullable final String productName,
+                             @JsonProperty("productCategory") @Nullable final String productCategory,
+                             @JsonProperty("billingPeriod") @Nullable final String billingPeriod,
+                             @JsonProperty("priceListName") @Nullable final String priceListName,
+                             @JsonProperty("subscriptionId") @Nullable final String subscriptionId,
+                             @JsonProperty("bundleId") @Nullable final String bundleId,
+                             @JsonProperty("effectiveDate") @Nullable final LocalDate effectiveDate,
+                             @JsonProperty("billingPolicy") @Nullable final String billingPolicy) {
+        this.dryRunAction = dryRunAction;
+        this.phaseType = phaseType;
+        this.productName = productName;
+        this.productCategory = productCategory;
+        this.billingPeriod = billingPeriod;
+        this.priceListName = priceListName;
+        this.subscriptionId = subscriptionId;
+        this.bundleId = bundleId;
+        this.effectiveDate = effectiveDate;
+        this.billingPolicy = billingPolicy;
+    }
+
+    public String getDryRunAction() {
+        return dryRunAction;
+    }
+
+    public String getPhaseType() {
+        return phaseType;
+    }
+
+    public String getProductName() {
+        return productName;
+    }
+
+    public String getProductCategory() {
+        return productCategory;
+    }
+
+    public String getBillingPeriod() {
+        return billingPeriod;
+    }
+
+    public String getPriceListName() {
+        return priceListName;
+    }
+
+    public String getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    public LocalDate getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    public String getBundleId() {
+        return bundleId;
+    }
+
+    public String getBillingPolicy() {
+        return billingPolicy;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof InvoiceDryRunJson)) {
+            return false;
+        }
+
+        final InvoiceDryRunJson that = (InvoiceDryRunJson) o;
+
+        if (billingPeriod != null ? !billingPeriod.equals(that.billingPeriod) : that.billingPeriod != null) {
+            return false;
+        }
+        if (billingPolicy != null ? !billingPolicy.equals(that.billingPolicy) : that.billingPolicy != null) {
+            return false;
+        }
+        if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
+            return false;
+        }
+        if (dryRunAction != null ? !dryRunAction.equals(that.dryRunAction) : that.dryRunAction != null) {
+            return false;
+        }
+        if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
+            return false;
+        }
+        if (phaseType != null ? !phaseType.equals(that.phaseType) : that.phaseType != null) {
+            return false;
+        }
+        if (priceListName != null ? !priceListName.equals(that.priceListName) : that.priceListName != null) {
+            return false;
+        }
+        if (productCategory != null ? !productCategory.equals(that.productCategory) : that.productCategory != null) {
+            return false;
+        }
+        if (productName != null ? !productName.equals(that.productName) : that.productName != null) {
+            return false;
+        }
+        if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = dryRunAction != null ? dryRunAction.hashCode() : 0;
+        result = 31 * result + (phaseType != null ? phaseType.hashCode() : 0);
+        result = 31 * result + (productName != null ? productName.hashCode() : 0);
+        result = 31 * result + (productCategory != null ? productCategory.hashCode() : 0);
+        result = 31 * result + (billingPeriod != null ? billingPeriod.hashCode() : 0);
+        result = 31 * result + (priceListName != null ? priceListName.hashCode() : 0);
+        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+        result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
+        result = 31 * result + (bundleId != null ? bundleId.hashCode() : 0);
+        result = 31 * result + (billingPolicy != null ? billingPolicy.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index 9399f63..bebfd31 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
@@ -30,6 +30,7 @@ import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicReference;
 
+import javax.annotation.Nullable;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -45,14 +46,23 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
+import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.PhaseType;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.SubscriptionApiException;
+import org.killbill.billing.entitlement.api.SubscriptionEventType;
+import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
@@ -60,6 +70,7 @@ import org.killbill.billing.invoice.api.InvoiceNotifier;
 import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.jaxrs.json.CustomFieldJson;
+import org.killbill.billing.jaxrs.json.InvoiceDryRunJson;
 import org.killbill.billing.jaxrs.json.InvoiceItemJson;
 import org.killbill.billing.jaxrs.json.InvoiceJson;
 import org.killbill.billing.jaxrs.json.InvoicePaymentJson;
@@ -81,6 +92,7 @@ import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.clock.Clock;
+import org.killbill.clock.ClockUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -254,7 +266,6 @@ public class InvoiceResource extends JaxRsResourceBase {
     @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid account id or target datetime supplied")})
     public Response createFutureInvoice(@QueryParam(QUERY_ACCOUNT_ID) final String accountId,
                                         @QueryParam(QUERY_TARGET_DATE) final String targetDateTime,
-                                        @QueryParam(QUERY_DRY_RUN) @DefaultValue("false") final Boolean dryRun,
                                         @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                         @HeaderParam(HDR_REASON) final String reason,
                                         @HeaderParam(HDR_COMMENT) final String comment,
@@ -263,12 +274,67 @@ public class InvoiceResource extends JaxRsResourceBase {
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
         final LocalDate inputDate = toLocalDate(UUID.fromString(accountId), targetDateTime, callContext);
 
-        final Invoice generatedInvoice = invoiceApi.triggerInvoiceGeneration(UUID.fromString(accountId), inputDate, dryRun,
-                                                                             callContext);
-        if (dryRun) {
-            return Response.status(Status.OK).entity(new InvoiceJson(generatedInvoice)).build();
-        } else {
+        try {
+            final Invoice generatedInvoice = invoiceApi.triggerInvoiceGeneration(UUID.fromString(accountId), inputDate, null,
+                                                                                 callContext);
             return uriBuilder.buildResponse(uriInfo, InvoiceResource.class, "getInvoice", generatedInvoice.getId());
+        } catch (InvoiceApiException e) {
+            if (e.getCode() == ErrorCode.INVOICE_NOTHING_TO_DO.getCode()) {
+                return Response.status(Status.NOT_FOUND).build();
+            }
+            throw e;
+        }
+    }
+
+    @Timed
+    @POST
+    @Path("/" + DRY_RUN)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Generate a dryRun invoice", response = InvoiceJson.class)
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid account id or target datetime supplied")})
+    public Response generateDryRunInvoice(@Nullable final InvoiceDryRunJson dryRunSubscriptionSpec,
+                                          @QueryParam(QUERY_ACCOUNT_ID) final String accountId,
+                                          @QueryParam(QUERY_TARGET_DATE) final String targetDateTime,
+                                          @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                          @HeaderParam(HDR_REASON) final String reason,
+                                          @HeaderParam(HDR_COMMENT) final String comment,
+                                          @javax.ws.rs.core.Context final HttpServletRequest request,
+                                          @javax.ws.rs.core.Context final UriInfo uriInfo) throws AccountApiException, InvoiceApiException {
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        final LocalDate inputDate = toLocalDate(UUID.fromString(accountId), targetDateTime, callContext);
+
+        // Passing a null or empty body means we are trying to generate an invoice with a (future) targetDate
+        // On the other hand if body is not null, we are attempting a dryRun subscription operation
+        if (dryRunSubscriptionSpec != null && dryRunSubscriptionSpec.getDryRunAction() != null) {
+            if (SubscriptionEventType.START_BILLING.toString().equals(dryRunSubscriptionSpec.getDryRunAction())) {
+                verifyNonNullOrEmpty(dryRunSubscriptionSpec.getProductName(), "DryRun subscription product category should be specified");
+                verifyNonNullOrEmpty(dryRunSubscriptionSpec.getBillingPeriod(), "DryRun subscription billingPeriod should be specified");
+                verifyNonNullOrEmpty(dryRunSubscriptionSpec.getProductCategory(), "DryRun subscription product category should be specified");
+                if (dryRunSubscriptionSpec.getProductCategory().equals(ProductCategory.ADD_ON)) {
+                    verifyNonNullOrEmpty(dryRunSubscriptionSpec.getBundleId(), "DryRun bundle ID should be specified");
+                }
+            } else if (SubscriptionEventType.CHANGE.toString().equals(dryRunSubscriptionSpec.getDryRunAction())) {
+                verifyNonNullOrEmpty(dryRunSubscriptionSpec.getProductName(), "DryRun subscription product category should be specified");
+                verifyNonNullOrEmpty(dryRunSubscriptionSpec.getBillingPeriod(), "DryRun subscription billingPeriod should be specified");
+                verifyNonNullOrEmpty(dryRunSubscriptionSpec.getSubscriptionId(), "DryRun subscriptionID should be specified");
+            }  else if (SubscriptionEventType.STOP_BILLING.toString().equals(dryRunSubscriptionSpec.getDryRunAction())) {
+                verifyNonNullOrEmpty(dryRunSubscriptionSpec.getSubscriptionId(), "DryRun subscriptionID should be specified");
+            }
+        }
+
+        final Account account = accountUserApi.getAccountById(UUID.fromString(accountId), callContext);
+
+        final DryRunArguments dryRunArguments = new DefaultDryRunArguments(dryRunSubscriptionSpec, account.getTimeZone(), clock);
+        try {
+            final Invoice generatedInvoice = invoiceApi.triggerInvoiceGeneration(UUID.fromString(accountId), inputDate, dryRunArguments,
+                                                                                 callContext);
+            return Response.status(Status.OK).entity(new InvoiceJson(generatedInvoice, true, null)).build();
+        } catch (InvoiceApiException e) {
+            if (e.getCode() == ErrorCode.INVOICE_NOTHING_TO_DO.getCode()) {
+                return Response.status(Status.NOT_FOUND).build();
+            }
+            throw e;
         }
     }
 
@@ -603,4 +669,81 @@ public class InvoiceResource extends JaxRsResourceBase {
     protected ObjectType getObjectType() {
         return ObjectType.INVOICE;
     }
+
+    private static class DefaultDryRunArguments implements DryRunArguments {
+
+        private final SubscriptionEventType action;
+        private final UUID subscriptionId;
+        private final DateTime effectiveDate;
+        private final PlanPhaseSpecifier specifier;
+        private final UUID bundleId;
+        private final BillingActionPolicy billingPolicy;
+
+        public DefaultDryRunArguments(final SubscriptionEventType action, final UUID subscriptionId, final UUID bundleId,
+                                      final PlanPhaseSpecifier specifier, final DateTime effectiveDate, final BillingActionPolicy billingPolicy) {
+            this.action = action;
+            this.subscriptionId = subscriptionId;
+            this.bundleId = bundleId;
+            this.effectiveDate = effectiveDate;
+            this.billingPolicy = billingPolicy;
+            this.specifier = specifier;
+        }
+
+        public DefaultDryRunArguments(final InvoiceDryRunJson input, final DateTimeZone accountTimeZone, final Clock clock) {
+            if (input == null) {
+                this.action = null;
+                this.subscriptionId = null;
+                this.effectiveDate = null;
+                this.specifier = null;
+                this.bundleId = null;
+                this.billingPolicy = null;
+            } else {
+                this.action = input.getDryRunAction() != null ? SubscriptionEventType.valueOf(input.getDryRunAction()) : null;
+                this.subscriptionId = input.getSubscriptionId() != null ? UUID.fromString(input.getSubscriptionId()) : null;
+                this.bundleId = input.getBundleId() != null ? UUID.fromString(input.getBundleId()) : null;
+                this.effectiveDate = input.getEffectiveDate() != null ? ClockUtil.computeDateTimeWithUTCReferenceTime(input.getEffectiveDate(), clock.getUTCNow().toLocalTime(), accountTimeZone, clock) : null;
+                this.billingPolicy = input.getBillingPolicy() != null ? BillingActionPolicy.valueOf(input.getBillingPolicy()) : null;
+                this.specifier = (input.getProductName() != null &&
+                                  input.getProductCategory() != null &&
+                                  input.getBillingPeriod() != null) ?
+                                 new PlanPhaseSpecifier(input.getProductName(),
+                                                        ProductCategory.valueOf(input.getProductCategory()),
+                                                        BillingPeriod.valueOf(input.getBillingPeriod()),
+                                                        input.getPriceListName(),
+                                                        input.getPhaseType() != null ? PhaseType.valueOf(input.getPhaseType()) : null) :
+                                 null;
+            }
+        }
+
+        @Override
+        public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+            return specifier;
+        }
+
+        @Override
+        public SubscriptionEventType getAction() {
+            return action;
+        }
+
+        @Override
+        public UUID getSubscriptionId() {
+            return subscriptionId;
+        }
+
+        @Override
+        public DateTime getEffectiveDate() {
+            return effectiveDate;
+        }
+
+        @Override
+        public UUID getBundleId() {
+            return bundleId;
+        }
+
+        @Override
+        public BillingActionPolicy getBillingActionPolicy() {
+            return billingPolicy;
+        }
+    }
+
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index a6d97d0..d7c5ab8 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -65,7 +65,6 @@ public interface JaxrsResource {
     public static final String QUERY_CALL_COMPLETION = "callCompletion";
     public static final String QUERY_USE_REQUESTED_DATE_FOR_BILLING = "useRequestedDateForBilling";
     public static final String QUERY_CALL_TIMEOUT = "callTimeoutSec";
-    public static final String QUERY_DRY_RUN = "dryRun";
     public static final String QUERY_TARGET_DATE = "targetDate";
     public static final String QUERY_BILLING_POLICY = "billingPolicy";
     public static final String QUERY_ENTITLEMENT_POLICY = "entitlementPolicy";
@@ -159,6 +158,7 @@ public interface JaxrsResource {
 
     public static final String INVOICE_PAYMENTS = "invoicePayments";
     public static final String INVOICE_PAYMENTS_PATH = PREFIX + "/" + INVOICE_PAYMENTS;
+    public static final String DRY_RUN = "dryRun";
 
     public static final String CHARGEBACKS = "chargebacks";
     public static final String CHARGEBACKS_PATH = PREFIX + "/" + CHARGEBACKS;
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
index 4cdfd36..20d06c9 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
@@ -17,6 +17,7 @@
 package org.killbill.billing.junction.plumbing.billing;
 
 import java.util.List;
+import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
@@ -60,7 +61,7 @@ public class BillCycleDayCalculator {
         this.subscriptionApi = subscriptionApi;
     }
 
-    protected int calculateBcd(final SubscriptionBaseBundle bundle, final SubscriptionBase subscription, final EffectiveSubscriptionInternalEvent transition, final Account account, final InternalCallContext context)
+    protected int calculateBcd(final UUID bundleId, final SubscriptionBase subscription, final EffectiveSubscriptionInternalEvent transition, final Account account, final InternalCallContext context)
             throws CatalogApiException, AccountApiException, SubscriptionBaseApiException {
 
         final Catalog catalog = catalogService.getFullCatalog();
@@ -85,11 +86,11 @@ public class BillCycleDayCalculator {
                                        phase.getPhaseType()),
                 transition.getRequestedTransitionTime());
 
-        return calculateBcdForAlignment(alignment, bundle, subscription, account, catalog, plan, context);
+        return calculateBcdForAlignment(alignment, bundleId, subscription, account, catalog, plan, context);
     }
 
     @VisibleForTesting
-    int calculateBcdForAlignment(final BillingAlignment alignment, final SubscriptionBaseBundle bundle, final SubscriptionBase subscription,
+    int calculateBcdForAlignment(final BillingAlignment alignment, final UUID bundleId, final SubscriptionBase subscription,
                                  final Account account, final Catalog catalog, final Plan plan, final InternalCallContext context) throws AccountApiException, SubscriptionBaseApiException, CatalogApiException {
         int result = 0;
         switch (alignment) {
@@ -100,7 +101,7 @@ public class BillCycleDayCalculator {
                 }
                 break;
             case BUNDLE:
-                final SubscriptionBase baseSub = subscriptionApi.getBaseSubscription(bundle.getId(), context);
+                final SubscriptionBase baseSub = subscriptionApi.getBaseSubscription(bundleId, context);
                 Plan basePlan = baseSub.getCurrentPlan();
                 if (basePlan == null) {
                     // The BP has been cancelled
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 9b4d9a2..07d0b3b 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
@@ -22,16 +22,6 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.joda.time.LocalDate;
-import org.killbill.billing.catalog.api.BillingMode;
-import org.killbill.billing.catalog.api.Usage;
-import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
-import org.killbill.clock.Clock;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
@@ -40,16 +30,23 @@ import org.killbill.billing.account.api.MutableAccountData;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogService;
+import org.killbill.billing.entitlement.EntitlementTransitionType;
+import org.killbill.billing.entitlement.api.SubscriptionEventType;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
+import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.junction.BillingEventSet;
 import org.killbill.billing.junction.BillingInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
+import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.Tag;
+import org.killbill.clock.Clock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
@@ -84,7 +81,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
     }
 
     @Override
-    public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(final UUID accountId, final InternalCallContext context) {
+    public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(final UUID accountId, final DryRunArguments dryRunArguments, final InternalCallContext context) {
         final List<SubscriptionBaseBundle> bundles = subscriptionApi.getBundlesForAccount(accountId, context);
         final DefaultBillingEventSet result = new DefaultBillingEventSet();
         result.setRecurrringBillingMode(catalogService.getCurrentCatalog().getRecurringBillingMode());
@@ -100,9 +97,11 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
                 return result; // billing is off, we are done
             }
 
-            addBillingEventsForBundles(bundles, account, context, result);
+            addBillingEventsForBundles(bundles, account, dryRunArguments, context, result);
         } catch (AccountApiException e) {
             log.warn("Failed while getting BillingEvent", e);
+        } catch (SubscriptionBaseApiException e) {
+            log.warn("Failed while getting BillingEvent", e);
         }
 
         // Pretty-print the events, before and after the blocking calculator does its magic
@@ -122,10 +121,29 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
         }
     }
 
-    private void addBillingEventsForBundles(final List<SubscriptionBaseBundle> bundles, final Account account, final InternalCallContext context,
-                                            final DefaultBillingEventSet result) {
+    private void addBillingEventsForBundles(final List<SubscriptionBaseBundle> bundles, final Account account, final DryRunArguments dryRunArguments, final InternalCallContext context,
+                                            final DefaultBillingEventSet result) throws SubscriptionBaseApiException {
+
+        final boolean dryRunMode = dryRunArguments != null;
+
+        // In dryRun mode, when we care about invoice generated for new BASE subscription, no such bundle exists yet; we still
+        // want to tap into subscriptionBase logic, so we make up a bundleId
+        if (dryRunArguments != null &&
+            dryRunArguments.getAction() == SubscriptionEventType.START_BILLING &&
+            dryRunArguments.getBundleId() == null) {
+            final UUID fakeBundleId = UUID.randomUUID();
+            final List<SubscriptionBase> subscriptions = subscriptionApi.getSubscriptionsForBundle(fakeBundleId, dryRunArguments, context);
+
+            addBillingEventsForSubscription(subscriptions, fakeBundleId, account, dryRunMode, context, result);
+
+        }
+
         for (final SubscriptionBaseBundle bundle : bundles) {
-            final List<SubscriptionBase> subscriptions = subscriptionApi.getSubscriptionsForBundle(bundle.getId(), context);
+            final DryRunArguments dryRunArgumentsForBundle = (dryRunArguments != null &&
+                                                             dryRunArguments.getBundleId() != null &&
+                                                             dryRunArguments.getBundleId().equals(bundle.getId())) ?
+                                                             dryRunArguments : null;
+            final List<SubscriptionBase> subscriptions = subscriptionApi.getSubscriptionsForBundle(bundle.getId(), dryRunArgumentsForBundle, context);
 
             //Check if billing is off for the bundle
             final List<Tag> bundleTags = tagApi.getTags(bundle.getId(), ObjectType.BUNDLE, context);
@@ -135,14 +153,18 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
                     result.getSubscriptionIdsWithAutoInvoiceOff().add(subscription.getId());
                 }
             } else { // billing is not off
-                addBillingEventsForSubscription(subscriptions, bundle, account, context, result);
+                addBillingEventsForSubscription(subscriptions, bundle.getId(), account, dryRunMode, context, result);
             }
         }
     }
 
-    private void addBillingEventsForSubscription(final List<SubscriptionBase> subscriptions, final SubscriptionBaseBundle bundle, final Account account, final InternalCallContext context, final DefaultBillingEventSet result) {
+    private void addBillingEventsForSubscription(final List<SubscriptionBase> subscriptions, final UUID bundleId, final Account account,
+                                                 final boolean dryRunMode,
+                                                 final InternalCallContext context,
+                                                 final DefaultBillingEventSet result) {
 
-        boolean updatedAccountBCD = false;
+        // If dryRun is specified, we don't want to to update the account BCD value, so we initialize the flag updatedAccountBCD to true
+        boolean updatedAccountBCD = dryRunMode;
         for (final SubscriptionBase subscription : subscriptions) {
 
             // The subscription did not even start, so there is nothing to do yet, we can skip and avoid some NPE down the line when calculating the BCD
@@ -152,7 +174,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
 
             for (final EffectiveSubscriptionInternalEvent transition : subscriptionApi.getBillingTransitions(subscription, context)) {
                 try {
-                    final int bcdLocal = bcdCalculator.calculateBcd(bundle, subscription, transition, account, context);
+                    final int bcdLocal = bcdCalculator.calculateBcd(bundleId, subscription, transition, account, context);
 
                     if (account.getBillCycleDayLocal() == 0 && !updatedAccountBCD) {
                         final MutableAccountData modifiedData = account.toMutableAccountData();
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillCycleDayCalculator.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillCycleDayCalculator.java
index 674aae2..5542ff3 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillCycleDayCalculator.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillCycleDayCalculator.java
@@ -62,7 +62,7 @@ public class TestBillCycleDayCalculator extends JunctionTestSuiteNoDB {
 
         final Account account = Mockito.mock(Account.class);
         Mockito.when(account.getTimeZone()).thenReturn(accountTimeZone);
-        final Integer billCycleDayLocal = billCycleDayCalculator.calculateBcdForAlignment(BillingAlignment.BUNDLE, bundle, subscription,
+        final Integer billCycleDayLocal = billCycleDayCalculator.calculateBcdForAlignment(BillingAlignment.BUNDLE, bundle.getId(), subscription,
                                                                                           account, catalog, null, internalCallContext);
 
         Assert.assertEquals(billCycleDayLocal, (Integer) expectedBCDUTC);
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 6cd947f..1180e15 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
@@ -16,10 +16,18 @@
 
 package org.killbill.billing.junction.plumbing.billing;
 
-import com.google.common.collect.ImmutableList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.MockCatalog;
 import org.killbill.billing.catalog.api.BillingAlignment;
 import org.killbill.billing.catalog.api.BillingMode;
@@ -33,32 +41,26 @@ import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.entitlement.dao.MockBlockingStateDao;
+import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
+import org.killbill.billing.invoice.api.DryRunArguments;
+import org.killbill.billing.junction.BillingEvent;
+import org.killbill.billing.junction.BillingEventSet;
+import org.killbill.billing.junction.DefaultBlockingState;
 import org.killbill.billing.junction.JunctionTestSuiteNoDB;
 import org.killbill.billing.mock.MockEffectiveSubscriptionEvent;
 import org.killbill.billing.mock.MockSubscription;
-import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.subscription.api.SubscriptionBase;
+import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.util.api.TagApiException;
-import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
-import org.killbill.billing.junction.BillingEvent;
-import org.killbill.billing.junction.BillingEventSet;
-import org.killbill.billing.junction.DefaultBlockingState;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.dao.MockTagDao;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.UUID;
+import com.google.common.collect.ImmutableList;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNull;
@@ -90,11 +92,11 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
         final List<SubscriptionBase> subscriptions = ImmutableList.<SubscriptionBase>of(subscription);
 
         Mockito.when(subscriptionInternalApi.getBundlesForAccount(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(bundles);
-        Mockito.when(subscriptionInternalApi.getSubscriptionsForBundle(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(subscriptions);
+        Mockito.when(subscriptionInternalApi.getSubscriptionsForBundle(Mockito.<UUID>any(), Mockito.<DryRunArguments>any(), Mockito.<InternalTenantContext>any())).thenReturn(subscriptions);
         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.getBillingTransitions(Mockito.<SubscriptionBase>any(),  Mockito.<InternalTenantContext>any())).thenReturn(effectiveSubscriptionTransitions);
         Mockito.when(subscriptionInternalApi.getAllTransitions(Mockito.<SubscriptionBase>any(), Mockito.<InternalTenantContext>any())).thenReturn(effectiveSubscriptionTransitions);
 
         catalog = ((MockCatalog) catalogService.getCurrentCatalog());
@@ -110,7 +112,7 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testBillingEventsEmpty() throws AccountApiException {
-        final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(new UUID(0L, 0L), internalCallContext);
+        final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(new UUID(0L, 0L), null, internalCallContext);
         Assert.assertEquals(events.size(), 0);
     }
 
@@ -123,7 +125,7 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
 
         final Account account = createAccount(10);
 
-        final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext);
+        final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, internalCallContext);
         checkFirstEvent(events, nextPlan, account.getBillCycleDayLocal(), subId, now, nextPhase, SubscriptionBaseTransitionType.CREATE.toString());
     }
 
@@ -137,7 +139,7 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
 
         catalog.setBillingAlignment(BillingAlignment.SUBSCRIPTION);
 
-        final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext);
+        final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, internalCallContext);
         // The expected BCD is when the subscription started since we skip the trial phase
         checkFirstEvent(events, nextPlan, subscription.getStartDate().getDayOfMonth(), subId, now, nextPhase, SubscriptionBaseTransitionType.CREATE.toString());
     }
@@ -150,7 +152,7 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
 
         final Account account = createAccount(32);
 
-        final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext);
+        final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, internalCallContext);
         // The expected BCD is the account BCD (account aligned by default)
         checkFirstEvent(events, nextPlan, 32, subId, now, nextPhase, SubscriptionBaseTransitionType.CREATE.toString());
     }
@@ -166,7 +168,7 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
         catalog.setBillingAlignment(BillingAlignment.BUNDLE);
         ((MockSubscription) subscription).setPlan(catalog.findPlan("PickupTrialEvergreen10USD", now));
 
-        final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext);
+        final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, internalCallContext);
         // The expected BCD is when the subscription started
         checkFirstEvent(events, nextPlan, subscription.getStartDate().getDayOfMonth(), subId, now, nextPhase, SubscriptionBaseTransitionType.CREATE.toString());
     }
@@ -179,10 +181,10 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
 
         final Account account = createAccount(32);
 
-        blockingStateDao.setBlockingState(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE,  DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)), clock, internalCallContext);
+        blockingStateDao.setBlockingState(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)), clock, internalCallContext);
         blockingStateDao.setBlockingState(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)), clock, internalCallContext);
 
-        final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext);
+        final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, internalCallContext);
 
         Assert.assertEquals(events.size(), 3);
         final Iterator<BillingEvent> it = events.iterator();
@@ -202,7 +204,7 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
 
         tagInternalApi.addTag(account.getId(), ObjectType.ACCOUNT, ControlTagType.AUTO_INVOICING_OFF.getId(), internalCallContext);
 
-        final BillingEventSet events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext);
+        final BillingEventSet events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, internalCallContext);
 
         assertEquals(events.isAccountAutoInvoiceOff(), true);
         assertEquals(events.size(), 0);
@@ -218,7 +220,7 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
 
         tagInternalApi.addTag(bunId, ObjectType.BUNDLE, ControlTagType.AUTO_INVOICING_OFF.getId(), internalCallContext);
 
-        final BillingEventSet events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext);
+        final BillingEventSet events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, internalCallContext);
 
         assertEquals(events.getSubscriptionIdsWithAutoInvoiceOff().size(), 1);
         assertEquals(events.getSubscriptionIdsWithAutoInvoiceOff().get(0), subId);
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
index 4b30b27..5811e10 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
@@ -183,7 +183,7 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
         // * 2013-08-07 to 2013-08-07 (block1Date)
         // * 2013-08-12 to 2013-08-12 (block2Date)
         // * 2013-08-15 to 2013-10-04 [2013-08-15 to 2013-10-01 (block3Date -> block4Date) and 2013-10-01 to 2013-10-04 (block4Date -> block5Date)]
-        final List<BillingEvent> events = ImmutableList.<BillingEvent>copyOf(billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext));
+        final List<BillingEvent> events = ImmutableList.<BillingEvent>copyOf(billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, internalCallContext));
         Assert.assertEquals(events.size(), 7);
         Assert.assertEquals(events.get(0).getTransitionType(), SubscriptionBaseTransitionType.CREATE);
         Assert.assertEquals(events.get(0).getEffectiveDate(), subscription.getStartDate());
@@ -259,7 +259,7 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
 
         // Expected blocking duration:
         // * 2013-08-07 to now [2013-08-07 to 2013-08-08 then 2013-08-08 to now]
-        final List<BillingEvent> events = ImmutableList.<BillingEvent>copyOf(billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext));
+        final List<BillingEvent> events = ImmutableList.<BillingEvent>copyOf(billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, internalCallContext));
         Assert.assertEquals(events.size(), 2);
         Assert.assertEquals(events.get(0).getTransitionType(), SubscriptionBaseTransitionType.CREATE);
         Assert.assertEquals(events.get(0).getEffectiveDate(), subscription.getStartDate());

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index f2d71f1..119e98c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.8.4</version>
+        <version>0.8.6</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.11.14-SNAPSHOT</version>
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
index 390991d..0b803c3 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
@@ -26,16 +26,20 @@ import java.util.UUID;
 import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
+import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.client.model.Account;
 import org.killbill.billing.client.model.AuditLog;
 import org.killbill.billing.client.model.Invoice;
+import org.killbill.billing.client.model.InvoiceDryRun;
 import org.killbill.billing.client.model.InvoiceItem;
 import org.killbill.billing.client.model.InvoicePayment;
 import org.killbill.billing.client.model.InvoicePayments;
 import org.killbill.billing.client.model.Invoices;
 import org.killbill.billing.client.model.Payment;
 import org.killbill.billing.client.model.PaymentMethod;
+import org.killbill.billing.entitlement.api.SubscriptionEventType;
 import org.killbill.billing.payment.provider.ExternalPaymentProviderPlugin;
 import org.killbill.billing.util.api.AuditLevel;
 import org.testng.Assert;
@@ -89,7 +93,7 @@ public class TestInvoice extends TestJaxrsBase {
 
         // Then create a dryRun Invoice
         final DateTime futureDate = clock.getUTCNow().plusMonths(1).plusDays(3);
-        killBillClient.createDryRunInvoice(accountJson.getAccountId(), futureDate, createdBy, reason, comment);
+        killBillClient.createDryRunInvoice(accountJson.getAccountId(), futureDate, null, createdBy, reason, comment);
 
         // The one more time with no DryRun
         killBillClient.createInvoice(accountJson.getAccountId(), futureDate, createdBy, reason, comment);
@@ -99,6 +103,21 @@ public class TestInvoice extends TestJaxrsBase {
         assertEquals(newInvoiceList.size(), 3);
     }
 
+
+    @Test(groups = "slow", description = "Can create a subscription in dryRun mode and get an invoice back")
+    public void testDryRunSubscriptionCreate() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        // "Assault-Rifle", BillingPeriod.ANNUAL, "rescue", BillingActionPolicy.IMMEDIATE,
+        final Account accountJson = createAccountWithDefaultPaymentMethod();
+        final InvoiceDryRun dryRunArg = new InvoiceDryRun(SubscriptionEventType.START_BILLING,
+                                                          null, "Assault-Rifle", ProductCategory.BASE, BillingPeriod.ANNUAL, null, null, null, null, null);
+        final Invoice dryRunInvoice = killBillClient.createDryRunInvoice(accountJson.getAccountId(), initialDate, dryRunArg, createdBy, reason, comment);
+        assertEquals(dryRunInvoice.getItems().size(), 1);
+
+    }
+
     @Test(groups = "slow", description = "Can retrieve invoice payments")
     public void testInvoicePayments() throws Exception {
         clock.setTime(new DateTime(2012, 4, 25, 0, 3, 42, 0));
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java b/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java
index 27bc7e6..4c194cc 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java
@@ -64,24 +64,26 @@ public class PlanAligner extends BaseAligner {
     /**
      * Returns the current and next phase for the subscription in creation
      *
-     * @param subscription  the subscription in creation (only the start date and the bundle start date are looked at)
-     * @param plan          the current Plan
-     * @param initialPhase  the initialPhase on which we should create that subscription. can be null
-     * @param priceList     the priceList
-     * @param requestedDate the requested date (only used to load the catalog)
-     * @param effectiveDate the effective creation date (driven by the catalog policy, i.e. when the creation occurs)
+     * @param alignStartDate  the subscription (align) startDate for the subscription
+     * @param bundleStartDate the bundle startDate used alignment
+     * @param plan            the current Plan
+     * @param initialPhase    the initialPhase on which we should create that subscription. can be null
+     * @param priceList       the priceList
+     * @param requestedDate   the requested date (only used to load the catalog)
+     * @param effectiveDate   the effective creation date (driven by the catalog policy, i.e. when the creation occurs)
      * @return the current and next phases
      * @throws CatalogApiException         for catalog errors
      * @throws org.killbill.billing.subscription.api.user.SubscriptionBaseApiException for subscription errors
      */
-    public TimedPhase[] getCurrentAndNextTimedPhaseOnCreate(final DefaultSubscriptionBase subscription,
+    public TimedPhase[] getCurrentAndNextTimedPhaseOnCreate(final DateTime alignStartDate,
+                                                            final DateTime bundleStartDate,
                                                             final Plan plan,
-                                                            final PhaseType initialPhase,
+                                                            @Nullable final PhaseType initialPhase,
                                                             final String priceList,
                                                             final DateTime requestedDate,
                                                             final DateTime effectiveDate) throws CatalogApiException, SubscriptionBaseApiException {
-        final List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getAlignStartDate(),
-                                                                   subscription.getBundleStartDate(),
+        final List<TimedPhase> timedPhases = getTimedPhaseOnCreate(alignStartDate,
+                                                                   bundleStartDate,
                                                                    plan,
                                                                    initialPhase,
                                                                    priceList,
@@ -187,7 +189,7 @@ public class PlanAligner extends BaseAligner {
     private List<TimedPhase> getTimedPhaseOnCreate(final DateTime subscriptionStartDate,
                                                    final DateTime bundleStartDate,
                                                    final Plan plan,
-                                                   final PhaseType initialPhase,
+                                                   @Nullable final PhaseType initialPhase,
                                                    final String priceList,
                                                    final DateTime requestedDate)
             throws CatalogApiException, SubscriptionBaseApiException {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/migration/DefaultSubscriptionBaseMigrationApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/migration/DefaultSubscriptionBaseMigrationApi.java
index cd0c710..af51ce0 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/migration/DefaultSubscriptionBaseMigrationApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/migration/DefaultSubscriptionBaseMigrationApi.java
@@ -198,7 +198,8 @@ public class DefaultSubscriptionBaseMigrationApi extends SubscriptionApiBase imp
 
             if (cur.getEventType() == EventType.PHASE) {
                 nextEventDate = nextEventDate != null && nextEventDate.compareTo(cur.getEventTime()) < 0 ? nextEventDate : cur.getEventTime();
-                final PhaseEvent nextPhaseEvent = PhaseEventData.createNextPhaseEvent(cur.getPhase().getName(), defaultSubscriptionBase, now, cur.getEventTime());
+                final PhaseEvent nextPhaseEvent = PhaseEventData.createNextPhaseEvent(defaultSubscriptionBase.getId(), defaultSubscriptionBase.getActiveVersion(),
+                                                                                      cur.getPhase().getName(), now, cur.getEventTime());
                 events.add(nextPhaseEvent);
 
 
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
index 2fdf041..7f67068 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
@@ -16,18 +16,24 @@
 
 package org.killbill.billing.subscription.api;
 
-import org.joda.time.DateTime;
+import java.util.List;
+import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanChangeResult;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBuilder;
+import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
 import org.killbill.billing.util.callcontext.CallContext;
-import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.util.callcontext.TenantContext;
 
 public interface SubscriptionBaseApiService {
 
@@ -37,7 +43,7 @@ public interface SubscriptionBaseApiService {
             throws SubscriptionBaseApiException;
 
     @Deprecated
-    public boolean recreatePlan(final DefaultSubscriptionBase subscription, final PlanPhaseSpecifier spec, final DateTime requestedDateWithMs, final CallContext context)
+    public boolean recreatePlan(DefaultSubscriptionBase subscription, PlanPhaseSpecifier spec, DateTime requestedDateWithMs, CallContext context)
             throws SubscriptionBaseApiException;
 
     public boolean cancel(DefaultSubscriptionBase subscription, CallContext context)
@@ -67,5 +73,26 @@ public interface SubscriptionBaseApiService {
                                          String priceList, BillingActionPolicy policy, CallContext context)
             throws SubscriptionBaseApiException;
 
-    public int cancelAddOnsIfRequired(final DefaultSubscriptionBase baseSubscription, final DateTime effectiveDate, final InternalCallContext context);
-}
+    public int cancelAddOnsIfRequired(final Product baseProduct, final UUID bundleId, final DateTime effectiveDate, final CallContext context);
+
+    public PlanChangeResult getPlanChangeResult(final DefaultSubscriptionBase subscription, final String productName,
+                                                final BillingPeriod term, final String priceList, final DateTime effectiveDate) throws SubscriptionBaseApiException;
+
+        //
+    // Lower level APIs for dryRun functionality
+    //
+    public List<SubscriptionBaseEvent> getEventsOnCreation(UUID subscriptionId, DateTime alignStartDate, DateTime bundleStartDate, long activeVersion,
+                                                           Plan plan, PhaseType initialPhase,
+                                                           String realPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate,
+                                                           boolean reCreate, TenantContext context)
+            throws CatalogApiException, SubscriptionBaseApiException;
+
+    public List<SubscriptionBaseEvent> getEventsOnChangePlan(DefaultSubscriptionBase subscription, Plan newPlan,
+                                                             String newPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate,
+                                                             boolean addCancellationAddOnForEventsIfRequired, TenantContext context)
+            throws CatalogApiException, SubscriptionBaseApiException;
+
+    public List<SubscriptionBaseEvent> getEventsOnCancelPlan(final DefaultSubscriptionBase subscription,
+                                                             final DateTime requestedDate, final DateTime effectiveDate, final DateTime processedDate,
+                                                             final boolean addCancellationAddOnForEventsIfRequired, final TenantContext context);
+}
\ No newline at end of file
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 29b1b20..9ba3261 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
@@ -16,6 +16,7 @@
 
 package org.killbill.billing.subscription.api.svcs;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -25,29 +26,25 @@ import java.util.UUID;
 import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
-import org.killbill.billing.util.cache.Cachable.CacheType;
-import org.killbill.billing.util.cache.CacheControllerDispatcher;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
 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.CatalogService;
 import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanChangeResult;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
-import org.killbill.clock.Clock;
-import org.killbill.clock.DefaultClock;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.entitlement.api.EntitlementAOStatusDryRun;
 import org.killbill.billing.entitlement.api.EntitlementAOStatusDryRun.DryRunChangeReason;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
+import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.subscription.api.SubscriptionApiBase;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
@@ -64,10 +61,17 @@ import org.killbill.billing.subscription.api.user.SubscriptionBuilder;
 import org.killbill.billing.subscription.engine.addon.AddonUtils;
 import org.killbill.billing.subscription.engine.dao.SubscriptionDao;
 import org.killbill.billing.subscription.engine.dao.model.SubscriptionBundleModelDao;
+import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
 import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.SourcePaginationBuilder;
+import org.killbill.clock.Clock;
+import org.killbill.clock.DefaultClock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
@@ -111,7 +115,6 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
             final Catalog catalog = catalogService.getFullCatalog();
             final Plan plan = catalog.findPlan(spec.getProductName(), spec.getBillingPeriod(), realPriceList, requestedDate);
-
             final PlanPhase phase = plan.getAllPhases()[0];
             if (phase == null) {
                 throw new SubscriptionBaseError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog",
@@ -123,39 +126,8 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                 throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BUNDLE, bundleId);
             }
 
-            DateTime bundleStartDate = null;
             final DefaultSubscriptionBase baseSubscription = (DefaultSubscriptionBase) dao.getBaseSubscription(bundleId, context);
-            switch (plan.getProduct().getCategory()) {
-                case BASE:
-                    if (baseSubscription != null) {
-                        if (baseSubscription.getState() == EntitlementState.ACTIVE) {
-                            throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundleId);
-                        }
-                    }
-                    bundleStartDate = requestedDate;
-                    break;
-                case ADD_ON:
-                    if (baseSubscription == null) {
-                        throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BP, bundleId);
-                    }
-                    if (effectiveDate.isBefore(baseSubscription.getStartDate())) {
-                        throw new SubscriptionBaseApiException(ErrorCode.SUB_INVALID_REQUESTED_DATE, effectiveDate.toString(), baseSubscription.getStartDate().toString());
-                    }
-                    addonUtils.checkAddonCreationRights(baseSubscription, plan);
-                    bundleStartDate = baseSubscription.getStartDate();
-                    break;
-                case STANDALONE:
-                    if (baseSubscription != null) {
-                        throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundleId);
-                    }
-                    // Not really but we don't care, there is no alignment for STANDALONE subscriptions
-                    bundleStartDate = requestedDate;
-                    break;
-                default:
-                    throw new SubscriptionBaseError(String.format("Can't create subscription of type %s",
-                                                                  plan.getProduct().getCategory().toString()));
-            }
-
+            final DateTime bundleStartDate = getBundleStartDateWithSanity(bundleId, baseSubscription, plan, requestedDate, effectiveDate);
             final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID));
             return apiService.createPlan(new SubscriptionBuilder()
                                                  .setId(UUID.randomUUID())
@@ -240,7 +212,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
     public static SubscriptionBaseBundle getActiveBundleForKeyNotException(final List<SubscriptionBaseBundle> existingBundles, final SubscriptionDao dao, final Clock clock, final InternalTenantContext context) {
         for (SubscriptionBaseBundle cur : existingBundles) {
-            final List<SubscriptionBase> subscriptions = dao.getSubscriptions(cur.getId(), context);
+            final List<SubscriptionBase> subscriptions = dao.getSubscriptions(cur.getId(), ImmutableList.<SubscriptionBaseEvent>of(), context);
             for (SubscriptionBase s : subscriptions) {
                 if (s.getCategory() == ProductCategory.ADD_ON) {
                     continue;
@@ -255,9 +227,18 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
     @Override
     public List<SubscriptionBase> getSubscriptionsForBundle(UUID bundleId,
-                                                            InternalTenantContext context) {
-        final List<SubscriptionBase> internalSubscriptions = dao.getSubscriptions(bundleId, context);
-        return createSubscriptionsForApiUse(internalSubscriptions);
+                                                            @Nullable final DryRunArguments dryRunArguments,
+                                                            InternalTenantContext context) throws SubscriptionBaseApiException {
+
+        final List<SubscriptionBaseEvent> outputDryRunEvents = new ArrayList<SubscriptionBaseEvent>();
+        final List<SubscriptionBase> outputSubscriptions = new ArrayList<SubscriptionBase>();
+
+        populateDryRunEvents(bundleId, dryRunArguments, outputDryRunEvents, outputSubscriptions, context);
+        final List<SubscriptionBase> result = dao.getSubscriptions(bundleId, outputDryRunEvents, context);
+        if (result != null && !result.isEmpty()) {
+            outputSubscriptions.addAll(result);
+        }
+        return createSubscriptionsForApiUse(outputSubscriptions);
     }
 
     @Override
@@ -328,23 +309,6 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
     }
 
     @Override
-    public DateTime getNextBillingDate(final UUID accountId, final InternalTenantContext context) {
-        final List<SubscriptionBaseBundle> bundles = getBundlesForAccount(accountId, context);
-        DateTime result = null;
-        for (final SubscriptionBaseBundle bundle : bundles) {
-            final List<SubscriptionBase> subscriptions = getSubscriptionsForBundle(bundle.getId(), context);
-            for (final SubscriptionBase subscription : subscriptions) {
-                final DateTime chargedThruDate = subscription.getChargedThroughDate();
-                if (result == null ||
-                    (chargedThruDate != null && chargedThruDate.isBefore(result))) {
-                    result = subscription.getChargedThroughDate();
-                }
-            }
-        }
-        return result;
-    }
-
-    @Override
     public List<EntitlementAOStatusDryRun> getDryRunChangePlanStatus(final UUID subscriptionId, @Nullable final String baseProductName, final DateTime requestedDate, final InternalTenantContext context) throws SubscriptionBaseApiException {
         final SubscriptionBase subscription = dao.getSubscriptionFromId(subscriptionId, context);
         if (subscription == null) {
@@ -356,7 +320,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
         final List<EntitlementAOStatusDryRun> result = new LinkedList<EntitlementAOStatusDryRun>();
 
-        final List<SubscriptionBase> bundleSubscriptions = dao.getSubscriptions(subscription.getBundleId(), context);
+        final List<SubscriptionBase> bundleSubscriptions = dao.getSubscriptions(subscription.getBundleId(), ImmutableList.<SubscriptionBaseEvent>of(), context);
         for (final SubscriptionBase cur : bundleSubscriptions) {
             if (cur.getId().equals(subscriptionId)) {
                 continue;
@@ -391,6 +355,128 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
         dao.updateBundleExternalKey(bundleId, newExternalKey, context);
     }
 
+    private void populateDryRunEvents(@Nullable final UUID bundleId,
+                                      @Nullable final DryRunArguments dryRunArguments,
+                                      final List<SubscriptionBaseEvent> outputDryRunEvents,
+                                      final List<SubscriptionBase> outputSubscriptions,
+                                      InternalTenantContext context) throws SubscriptionBaseApiException {
+        if (dryRunArguments == null || dryRunArguments.getAction() == null) {
+            return;
+        }
+
+        final DateTime utcNow = clock.getUTCNow();
+        List<SubscriptionBaseEvent> dryRunEvents = null;
+        try {
+            final PlanPhaseSpecifier inputSpec = dryRunArguments.getPlanPhaseSpecifier();
+            final String realPriceList = (inputSpec != null && inputSpec.getPriceListName() != null) ? inputSpec.getPriceListName() : PriceListSet.DEFAULT_PRICELIST_NAME;
+            final Catalog catalog = catalogService.getFullCatalog();
+            final Plan plan = (inputSpec != null && inputSpec.getProductName() != null && inputSpec.getBillingPeriod() != null) ?
+                              catalog.findPlan(inputSpec.getProductName(), inputSpec.getBillingPeriod(), realPriceList, utcNow) : null;
+            final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID));
+
+            if (dryRunArguments != null) {
+                switch (dryRunArguments.getAction()) {
+                    case START_BILLING:
+
+                        final DefaultSubscriptionBase baseSubscription = (DefaultSubscriptionBase) dao.getBaseSubscription(bundleId, context);
+                        final DateTime startEffectiveDate = dryRunArguments.getEffectiveDate() != null ? dryRunArguments.getEffectiveDate() : utcNow;
+                        final DateTime bundleStartDate = getBundleStartDateWithSanity(bundleId, baseSubscription, plan, startEffectiveDate, startEffectiveDate);
+                        final UUID subscriptionId = UUID.randomUUID();
+                        dryRunEvents = apiService.getEventsOnCreation(subscriptionId, startEffectiveDate, bundleStartDate, 1L, plan, inputSpec.getPhaseType(), realPriceList,
+                                                                      utcNow, startEffectiveDate, utcNow, false, context.toTenantContext(tenantId));
+                        final SubscriptionBuilder builder = new SubscriptionBuilder()
+                                .setId(subscriptionId)
+                                .setBundleId(bundleId)
+                                .setCategory(plan.getProduct().getCategory())
+                                .setBundleStartDate(bundleStartDate)
+                                .setAlignStartDate(startEffectiveDate);
+                        final DefaultSubscriptionBase newSubscription = new DefaultSubscriptionBase(builder, apiService, clock);
+                        newSubscription.rebuildTransitions(dryRunEvents, catalog);
+                        outputSubscriptions.add(newSubscription);
+                        break;
+
+                    case CHANGE:
+                        final DefaultSubscriptionBase subscriptionForChange = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), context);
+                        DateTime changeEffectiveDate = dryRunArguments.getEffectiveDate();
+                        if (changeEffectiveDate == null) {
+                            BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
+                            if (policy == null) {
+                                final PlanChangeResult planChangeResult = apiService.getPlanChangeResult(subscriptionForChange,
+                                                                                                         dryRunArguments.getPlanPhaseSpecifier().getProductName(),
+                                                                                                         dryRunArguments.getPlanPhaseSpecifier().getBillingPeriod(),
+                                                                                                         dryRunArguments.getPlanPhaseSpecifier().getPriceListName(), utcNow);
+                                policy = planChangeResult.getPolicy();
+                            }
+                            changeEffectiveDate = subscriptionForChange.getPlanChangeEffectiveDate(policy);
+                        }
+                        dryRunEvents = apiService.getEventsOnChangePlan(subscriptionForChange, plan, realPriceList, utcNow, changeEffectiveDate, utcNow, true, context.toTenantContext(tenantId));
+                        break;
+
+                    case STOP_BILLING:
+                        final DefaultSubscriptionBase subscriptionForCancellation = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), context);
+                        DateTime cancelEffectiveDate = dryRunArguments.getEffectiveDate();
+                        if (dryRunArguments.getEffectiveDate() == null) {
+                            BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
+                            if (policy == null) {
+
+                                final Plan currentPlan = subscriptionForCancellation.getCurrentPlan();
+                                final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
+                                                                                            currentPlan.getProduct().getCategory(),
+                                                                                            subscriptionForCancellation.getCurrentPlan().getRecurringBillingPeriod(),
+                                                                                            subscriptionForCancellation.getCurrentPriceList().getName(),
+                                                                                            subscriptionForCancellation.getCurrentPhase().getPhaseType());
+                                policy = catalogService.getFullCatalog().planCancelPolicy(spec, utcNow);
+                            }
+                            cancelEffectiveDate = subscriptionForCancellation.getPlanChangeEffectiveDate(policy);
+                        }
+                        dryRunEvents = apiService.getEventsOnCancelPlan(subscriptionForCancellation, utcNow, cancelEffectiveDate, utcNow, true, context.toTenantContext(tenantId));
+                        break;
+
+                    default:
+                        throw new IllegalArgumentException("Unexpected dryRunArguments action " + dryRunArguments.getAction());
+                }
+            }
+        } catch (CatalogApiException e) {
+            throw new SubscriptionBaseApiException(e);
+        }
+        if (dryRunEvents != null && !dryRunEvents.isEmpty()) {
+            outputDryRunEvents.addAll(dryRunEvents);
+        }
+    }
+
+    private DateTime getBundleStartDateWithSanity(final UUID bundleId, @Nullable final DefaultSubscriptionBase baseSubscription, final Plan plan,
+                                                  final DateTime requestedDate, final DateTime effectiveDate) throws SubscriptionBaseApiException, CatalogApiException {
+        switch (plan.getProduct().getCategory()) {
+            case BASE:
+                if (baseSubscription != null &&
+                    baseSubscription.getState() == EntitlementState.ACTIVE) {
+                    throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundleId);
+                }
+                return requestedDate;
+
+            case ADD_ON:
+                if (baseSubscription == null) {
+                    throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BP, bundleId);
+                }
+                if (effectiveDate.isBefore(baseSubscription.getStartDate())) {
+                    throw new SubscriptionBaseApiException(ErrorCode.SUB_INVALID_REQUESTED_DATE, effectiveDate.toString(), baseSubscription.getStartDate().toString());
+                }
+                addonUtils.checkAddonCreationRights(baseSubscription, plan);
+                return baseSubscription.getStartDate();
+
+            case STANDALONE:
+                if (baseSubscription != null) {
+                    throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundleId);
+                }
+                // Not really but we don't care, there is no alignment for STANDALONE subscriptions
+                return requestedDate;
+
+            default:
+                throw new SubscriptionBaseError(String.format("Can't create subscription of type %s",
+                                                              plan.getProduct().getCategory().toString()));
+        }
+    }
+
     private List<EffectiveSubscriptionInternalEvent> convertEffectiveSubscriptionInternalEventFromSubscriptionTransitions(final SubscriptionBase subscription,
                                                                                                                           final InternalTenantContext context, final List<SubscriptionBaseTransition> transitions) {
         return ImmutableList.<EffectiveSubscriptionInternalEvent>copyOf(Collections2.transform(transitions, new Function<SubscriptionBaseTransition, EffectiveSubscriptionInternalEvent>() {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimelineApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimelineApi.java
index 44119f5..01b74ae 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimelineApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimelineApi.java
@@ -57,6 +57,7 @@ import org.killbill.clock.Clock;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 import com.google.inject.name.Named;
 
@@ -115,7 +116,9 @@ public class DefaultSubscriptionBaseTimelineApi extends SubscriptionApiBase impl
             if (bundle == null) {
                 throw new SubscriptionBaseRepairException(ErrorCode.SUB_REPAIR_UNKNOWN_BUNDLE, descBundle);
             }
-            final List<SubscriptionDataRepair> subscriptions = convertToSubscriptionsDataRepair(dao.getSubscriptions(bundle.getId(), internalCallContextFactory.createInternalTenantContext(context)));
+            final List<SubscriptionDataRepair> subscriptions = convertToSubscriptionsDataRepair(dao.getSubscriptions(bundle.getId(),
+                                                                                                                     ImmutableList.<SubscriptionBaseEvent>of(),
+                                                                                                                     internalCallContextFactory.createInternalTenantContext(context)));
             if (subscriptions.size() == 0) {
                 throw new SubscriptionBaseRepairException(ErrorCode.SUB_REPAIR_NO_ACTIVE_SUBSCRIPTIONS, bundle.getId());
             }
@@ -149,7 +152,7 @@ public class DefaultSubscriptionBaseTimelineApi extends SubscriptionApiBase impl
             }
 
             // Subscriptions are ordered with BASE subscription first-- if exists
-            final List<SubscriptionDataRepair> subscriptions = convertToSubscriptionsDataRepair(dao.getSubscriptions(input.getId(), tenantContext));
+            final List<SubscriptionDataRepair> subscriptions = convertToSubscriptionsDataRepair(dao.getSubscriptions(input.getId(), ImmutableList.<SubscriptionBaseEvent>of(), tenantContext));
             if (subscriptions.size() == 0) {
                 throw new SubscriptionBaseRepairException(ErrorCode.SUB_REPAIR_NO_ACTIVE_SUBSCRIPTIONS, input.getId());
             }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/RepairSubscriptionApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/RepairSubscriptionApiService.java
index 2d1da3d..6a0efd4 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/RepairSubscriptionApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/RepairSubscriptionApiService.java
@@ -16,10 +16,11 @@
 
 package org.killbill.billing.subscription.api.timeline;
 
-import org.joda.time.DateTime;
+import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.catalog.api.CatalogService;
-import org.killbill.clock.Clock;
+import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.subscription.alignment.PlanAligner;
 import org.killbill.billing.subscription.api.SubscriptionBaseApiService;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
@@ -27,8 +28,9 @@ import org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseApiServ
 import org.killbill.billing.subscription.engine.addon.AddonUtils;
 import org.killbill.billing.subscription.engine.dao.SubscriptionDao;
 import org.killbill.billing.subscription.glue.DefaultSubscriptionModule;
-import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.clock.Clock;
 
 import com.google.inject.Inject;
 import com.google.inject.name.Named;
@@ -47,7 +49,7 @@ public class RepairSubscriptionApiService extends DefaultSubscriptionBaseApiServ
 
     // Nothing to do for repair as we pass all the repair events in the stream
     @Override
-    public int cancelAddOnsIfRequired(final DefaultSubscriptionBase baseSubscription, final DateTime effectiveDate, final InternalCallContext context) {
+    public int cancelAddOnsIfRequired(final Product baseProduct, final UUID bundleId, final DateTime effectiveDate, final CallContext context) {
         return 0;
     }
 }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
index eb47278..c7d0a18 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
@@ -118,7 +118,7 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
 
             case PHASE:
                 newEvent = firstEvent ? new ApiEventTransfer(apiBuilder) :
-                           PhaseEventData.createNextPhaseEvent(currentPhase.getName(), subscription, clock.getUTCNow(), effectiveDate);
+                           PhaseEventData.createNextPhaseEvent(subscription.getId(), subscription.getActiveVersion(), currentPhase.getName(), clock.getUTCNow(), effectiveDate);
                 break;
 
             // Ignore these events except if it's the first event for the new subscription
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
index a75c96a..1d529d5 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
@@ -23,10 +23,10 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.CatalogApiException;
@@ -41,8 +41,6 @@ import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
-import org.killbill.clock.Clock;
-import org.killbill.clock.DefaultClock;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.subscription.alignment.PlanAligner;
 import org.killbill.billing.subscription.alignment.TimedPhase;
@@ -63,7 +61,11 @@ import org.killbill.billing.subscription.events.user.ApiEventUncancel;
 import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.clock.Clock;
+import org.killbill.clock.DefaultClock;
 
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiService {
@@ -134,29 +136,8 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
 
         try {
-            final TimedPhase[] curAndNextPhases = planAligner.getCurrentAndNextTimedPhaseOnCreate(subscription, plan, initialPhase, realPriceList, requestedDate, effectiveDate);
-
-            final ApiEventBuilder createBuilder = new ApiEventBuilder()
-                    .setSubscriptionId(subscription.getId())
-                    .setEventPlan(plan.getName())
-                    .setEventPlanPhase(curAndNextPhases[0].getPhase().getName())
-                    .setEventPriceList(realPriceList)
-                    .setActiveVersion(subscription.getActiveVersion())
-                    .setProcessedDate(processedDate)
-                    .setEffectiveDate(effectiveDate)
-                    .setRequestedDate(requestedDate)
-                    .setFromDisk(true);
-            final ApiEvent creationEvent = (reCreate) ? new ApiEventReCreate(createBuilder) : new ApiEventCreate(createBuilder);
-
-            final TimedPhase nextTimedPhase = curAndNextPhases[1];
-            final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
-                                              PhaseEventData.createNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, processedDate, nextTimedPhase.getStartPhase()) :
-                                              null;
-            final List<SubscriptionBaseEvent> events = new ArrayList<SubscriptionBaseEvent>();
-            events.add(creationEvent);
-            if (nextPhaseEvent != null) {
-                events.add(nextPhaseEvent);
-            }
+            final List<SubscriptionBaseEvent> events = getEventsOnCreation(subscription.getId(), subscription.getAlignStartDate(), subscription.getBundleStartDate(), subscription.getActiveVersion(),
+                                                                           plan, initialPhase, realPriceList, requestedDate, effectiveDate, processedDate, reCreate, context);
             if (reCreate) {
                 dao.recreateSubscription(subscription, events, internalCallContext);
             } else {
@@ -168,6 +149,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         }
     }
 
+
     @Override
     public boolean cancel(final DefaultSubscriptionBase subscription, final CallContext context) throws SubscriptionBaseApiException {
 
@@ -220,27 +202,23 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
     private boolean doCancelPlan(final DefaultSubscriptionBase subscription, final DateTime now, final DateTime effectiveDate, final CallContext context) throws SubscriptionBaseApiException {
         validateEffectiveDate(subscription, effectiveDate);
 
-        final SubscriptionBaseEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
-                                                                             .setSubscriptionId(subscription.getId())
-                                                                             .setActiveVersion(subscription.getActiveVersion())
-                                                                             .setProcessedDate(now)
-                                                                             .setEffectiveDate(effectiveDate)
-                                                                             .setRequestedDate(now)
-                                                                             .setFromDisk(true));
-
+        final List<SubscriptionBaseEvent> cancelEvents = getEventsOnCancelPlan(subscription, now, effectiveDate, now, false, context);
         final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
-        dao.cancelSubscription(subscription, cancelEvent, internalCallContext, 0);
+        // cancelEvents will contain only one item
+        dao.cancelSubscription(subscription, cancelEvents.get(0), internalCallContext, 0);
         subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), catalogService.getFullCatalog());
 
         if (subscription.getCategory() == ProductCategory.BASE) {
-            cancelAddOnsIfRequired(subscription, effectiveDate, internalCallContext);
+            final Product baseProduct = (subscription.getState() == EntitlementState.CANCELLED) ? null : subscription.getCurrentPlan().getProduct();
+            cancelAddOnsIfRequired(baseProduct, subscription.getBundleId(), effectiveDate, context);
         }
 
         final boolean isImmediate = subscription.getState() == EntitlementState.CANCELLED;
         return isImmediate;
     }
 
-    @Override
+
+        @Override
     public boolean uncancel(final DefaultSubscriptionBase subscription, final CallContext context) throws SubscriptionBaseApiException {
         if (!subscription.isSubscriptionFutureCancelled()) {
             throw new SubscriptionBaseApiException(ErrorCode.SUB_UNCANCEL_BAD_STATE, subscription.getId().toString());
@@ -260,7 +238,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
         final TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now, now);
         final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
-                                          PhaseEventData.createNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
+                                          PhaseEventData.createNextPhaseEvent(subscription.getId(), subscription.getActiveVersion(), nextTimedPhase.getPhase().getName(), now, nextTimedPhase.getStartPhase()) :
                                           null;
         if (nextPhaseEvent != null) {
             uncancelEvents.add(nextPhaseEvent);
@@ -291,6 +269,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         }
     }
 
+
     @Override
     public DateTime changePlanWithRequestedDate(final DefaultSubscriptionBase subscription, final String productName, final BillingPeriod term,
                                                 final String priceList, final DateTime requestedDateWithMs, final CallContext context) throws SubscriptionBaseApiException {
@@ -323,8 +302,9 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         }
     }
 
-    private PlanChangeResult getPlanChangeResult(final DefaultSubscriptionBase subscription, final String productName,
-                                                 final BillingPeriod term, final String priceList, final DateTime effectiveDate) throws SubscriptionBaseApiException {
+    @Override
+    public PlanChangeResult getPlanChangeResult(final DefaultSubscriptionBase subscription, final String productName,
+                                                final BillingPeriod term, final String priceList, final DateTime effectiveDate) throws SubscriptionBaseApiException {
         final PlanChangeResult planChangeResult;
         try {
             final Product destProduct = catalogService.getFullCatalog().findProduct(productName, effectiveDate);
@@ -358,7 +338,57 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
         final Plan newPlan = catalogService.getFullCatalog().findPlan(newProductName, newBillingPeriod, newPriceList, effectiveDate, subscription.getStartDate());
 
-        final TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnChange(subscription, newPlan, newPriceList, now, effectiveDate);
+        final List<SubscriptionBaseEvent> changeEvents = getEventsOnChangePlan(subscription, newPlan, newPriceList, now, effectiveDate, now, false, context);
+        final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
+        dao.changePlan(subscription, changeEvents, internalCallContext);
+        subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), catalogService.getFullCatalog());
+
+        if (subscription.getCategory() == ProductCategory.BASE) {
+            final Product baseProduct = (subscription.getState() == EntitlementState.CANCELLED) ? null : subscription.getCurrentPlan().getProduct();
+            cancelAddOnsIfRequired(baseProduct, subscription.getBundleId(), effectiveDate, context);
+        }
+        return effectiveDate;
+    }
+
+    @Override
+    public List<SubscriptionBaseEvent> getEventsOnCreation(final UUID subscriptionId, final DateTime alignStartDate, final DateTime bundleStartDate, final long activeVersion,
+                                                           final Plan plan, final PhaseType initialPhase,
+                                                           final String realPriceList, final DateTime requestedDate, final DateTime effectiveDate, final DateTime processedDate,
+                                                           final boolean reCreate, final TenantContext context) throws CatalogApiException, SubscriptionBaseApiException {
+        final TimedPhase[] curAndNextPhases = planAligner.getCurrentAndNextTimedPhaseOnCreate(alignStartDate, bundleStartDate, plan, initialPhase,
+                                                                                              realPriceList, requestedDate, effectiveDate);
+
+        final ApiEventBuilder createBuilder = new ApiEventBuilder()
+                .setSubscriptionId(subscriptionId)
+                .setEventPlan(plan.getName())
+                .setEventPlanPhase(curAndNextPhases[0].getPhase().getName())
+                .setEventPriceList(realPriceList)
+                .setActiveVersion(activeVersion)
+                .setProcessedDate(processedDate)
+                .setEffectiveDate(effectiveDate)
+                .setRequestedDate(requestedDate)
+                .setFromDisk(true);
+        final ApiEvent creationEvent = (reCreate) ? new ApiEventReCreate(createBuilder) : new ApiEventCreate(createBuilder);
+
+        final TimedPhase nextTimedPhase = curAndNextPhases[1];
+        final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
+                                          PhaseEventData.createNextPhaseEvent(subscriptionId, activeVersion, nextTimedPhase.getPhase().getName(), processedDate, nextTimedPhase.getStartPhase()) :
+                                          null;
+        final List<SubscriptionBaseEvent> events = new ArrayList<SubscriptionBaseEvent>();
+        events.add(creationEvent);
+        if (nextPhaseEvent != null) {
+            events.add(nextPhaseEvent);
+        }
+        return events;
+    }
+
+
+    @Override
+    public List<SubscriptionBaseEvent> getEventsOnChangePlan(final DefaultSubscriptionBase subscription, final Plan newPlan,
+                                                             final String newPriceList, final DateTime requestedDate, final DateTime effectiveDate, final DateTime processedDate,
+                                                             final boolean addCancellationAddOnForEventsIfRequired, final TenantContext context) throws CatalogApiException, SubscriptionBaseApiException {
+
+        final TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnChange(subscription, newPlan, newPriceList, requestedDate, effectiveDate);
 
         final SubscriptionBaseEvent changeEvent = new ApiEventChange(new ApiEventBuilder()
                                                                              .setSubscriptionId(subscription.getId())
@@ -366,14 +396,15 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                                                                              .setEventPlanPhase(currentTimedPhase.getPhase().getName())
                                                                              .setEventPriceList(newPriceList)
                                                                              .setActiveVersion(subscription.getActiveVersion())
-                                                                             .setProcessedDate(now)
+                                                                             .setProcessedDate(processedDate)
                                                                              .setEffectiveDate(effectiveDate)
-                                                                             .setRequestedDate(now)
+                                                                             .setRequestedDate(requestedDate)
                                                                              .setFromDisk(true));
 
-        final TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList, now, effectiveDate);
+        final TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList, processedDate, effectiveDate);
         final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
-                                          PhaseEventData.createNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
+                                          PhaseEventData.createNextPhaseEvent(subscription.getId(), subscription.getActiveVersion(),
+                                                                              nextTimedPhase.getPhase().getName(), processedDate, nextTimedPhase.getStartPhase()) :
                                           null;
 
         final List<SubscriptionBaseEvent> changeEvents = new ArrayList<SubscriptionBaseEvent>();
@@ -383,20 +414,36 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
             changeEvents.add(nextPhaseEvent);
         }
 
-        final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
-        dao.changePlan(subscription, changeEvents, internalCallContext);
-        subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), catalogService.getFullCatalog());
-
-        if (subscription.getCategory() == ProductCategory.BASE) {
-            cancelAddOnsIfRequired(subscription, effectiveDate, internalCallContext);
+        if (subscription.getCategory() == ProductCategory.BASE && addCancellationAddOnForEventsIfRequired) {
+            final Product currentBaseProduct = changeEvent.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0 ? newPlan.getProduct() : subscription.getCurrentPlan().getProduct();
+            addCancellationAddOnForEventsIfRequired(changeEvents, currentBaseProduct, subscription.getBundleId(), requestedDate, effectiveDate, processedDate, context);
         }
+        return changeEvents;
+    }
 
-        final boolean isChangeImmediate = subscription.getCurrentPlan().getProduct().getName().equals(newProductName) &&
-                                          subscription.getCurrentPlan().getRecurringBillingPeriod() == newBillingPeriod;
-        return effectiveDate;
+    @Override
+    public List<SubscriptionBaseEvent> getEventsOnCancelPlan(final DefaultSubscriptionBase subscription,
+                                                             final DateTime requestedDate, final DateTime effectiveDate, final DateTime processedDate,
+                                                             final boolean addCancellationAddOnForEventsIfRequired, final TenantContext context) {
+
+        final List<SubscriptionBaseEvent> cancelEvents = new ArrayList<SubscriptionBaseEvent>();
+        final SubscriptionBaseEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
+                                                                             .setSubscriptionId(subscription.getId())
+                                                                             .setActiveVersion(subscription.getActiveVersion())
+                                                                             .setProcessedDate(processedDate)
+                                                                             .setEffectiveDate(effectiveDate)
+                                                                             .setRequestedDate(requestedDate)
+                                                                             .setFromDisk(true));
+        cancelEvents.add(cancelEvent);
+        if (subscription.getCategory() == ProductCategory.BASE && addCancellationAddOnForEventsIfRequired) {
+            final Product currentBaseProduct = cancelEvent.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0 ? null : subscription.getCurrentPlan().getProduct();
+            addCancellationAddOnForEventsIfRequired(cancelEvents, currentBaseProduct, subscription.getBundleId(), requestedDate, effectiveDate, processedDate, context);
+        }
+        return cancelEvents;
     }
 
-    public int cancelAddOnsIfRequired(final DefaultSubscriptionBase baseSubscription, final DateTime effectiveDate, final InternalCallContext context) {
+
+    public int cancelAddOnsIfRequired(final Product baseProduct, final UUID bundleId, final DateTime effectiveDate, final CallContext context) {
 
         // If cancellation/change occur in the future, there is nothing to do
         final DateTime now = clock.getUTCNow();
@@ -404,12 +451,22 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
             return 0;
         }
 
-        final Product baseProduct = (baseSubscription.getState() == EntitlementState.CANCELLED) ? null : baseSubscription.getCurrentPlan().getProduct();
+        final List<SubscriptionBaseEvent> cancelEvents = new LinkedList<SubscriptionBaseEvent>();
+        final List<DefaultSubscriptionBase> subscriptionsToBeCancelled = addCancellationAddOnForEventsIfRequired(cancelEvents, baseProduct, bundleId, now, effectiveDate, now, context);
+        if (!subscriptionsToBeCancelled.isEmpty()) {
+            final InternalCallContext internalCallContext = createCallContextFromBundleId(bundleId, context);
+            dao.cancelSubscriptions(subscriptionsToBeCancelled, cancelEvents, internalCallContext);
+        }
+        return subscriptionsToBeCancelled.size();
+    }
 
-        final List<SubscriptionBase> subscriptions = dao.getSubscriptions(baseSubscription.getBundleId(), context);
+    private List<DefaultSubscriptionBase> addCancellationAddOnForEventsIfRequired(final List<SubscriptionBaseEvent> events, final Product baseProduct, final UUID bundleId,
+                                                                                  final DateTime requestedDate, final DateTime effectiveDate, final DateTime processedDate, final TenantContext context) {
 
-        final List<DefaultSubscriptionBase> subscriptionsToBeCancelled = new LinkedList<DefaultSubscriptionBase>();
-        final List<SubscriptionBaseEvent> cancelEvents = new LinkedList<SubscriptionBaseEvent>();
+        final List<DefaultSubscriptionBase> subscriptionsToBeCancelled = new ArrayList<DefaultSubscriptionBase>();
+
+        final InternalTenantContext internalCallContext = createTenantContextFromBundleId(bundleId, context);
+        final List<SubscriptionBase> subscriptions = dao.getSubscriptions(bundleId, ImmutableList.<SubscriptionBaseEvent>of(), internalCallContext);
 
         for (final SubscriptionBase subscription : subscriptions) {
             final DefaultSubscriptionBase cur = (DefaultSubscriptionBase) subscription;
@@ -428,17 +485,15 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                 final SubscriptionBaseEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
                                                                                      .setSubscriptionId(cur.getId())
                                                                                      .setActiveVersion(cur.getActiveVersion())
-                                                                                     .setProcessedDate(now)
+                                                                                     .setProcessedDate(processedDate)
                                                                                      .setEffectiveDate(effectiveDate)
-                                                                                     .setRequestedDate(now)
+                                                                                     .setRequestedDate(requestedDate)
                                                                                      .setFromDisk(true));
                 subscriptionsToBeCancelled.add(cur);
-                cancelEvents.add(cancelEvent);
+                events.add(cancelEvent);
             }
         }
-
-        dao.cancelSubscriptions(subscriptionsToBeCancelled, cancelEvents, context);
-        return subscriptionsToBeCancelled.size();
+        return subscriptionsToBeCancelled;
     }
 
     private void validateEffectiveDate(final DefaultSubscriptionBase subscription, final DateTime effectiveDate) throws SubscriptionBaseApiException {
@@ -462,4 +517,8 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
     private InternalCallContext createCallContextFromBundleId(final UUID bundleId, final CallContext context) {
         return internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, context);
     }
+
+    private InternalTenantContext createTenantContextFromBundleId(final UUID bundleId, final TenantContext context) {
+        return internalCallContextFactory.createInternalTenantContext(bundleId, ObjectType.BUNDLE, context);
+    }
 }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
index c38610b..8178614 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
@@ -21,8 +21,11 @@ package org.killbill.billing.subscription.engine.core;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
 import org.killbill.billing.platform.api.LifecycleHandlerType;
 import org.killbill.billing.platform.api.LifecycleHandlerType.LifecycleLevel;
@@ -41,9 +44,13 @@ import org.killbill.billing.subscription.events.phase.PhaseEvent;
 import org.killbill.billing.subscription.events.phase.PhaseEventData;
 import org.killbill.billing.subscription.events.user.ApiEvent;
 import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.UserType;
+import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.bus.api.PersistentBus.EventBusException;
 import org.killbill.clock.Clock;
@@ -68,27 +75,31 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
     private final Clock clock;
     private final SubscriptionDao dao;
     private final PlanAligner planAligner;
-    private final AddonUtils addonUtils;
     private final PersistentBus eventBus;
     private final NotificationQueueService notificationQueueService;
     private final InternalCallContextFactory internalCallContextFactory;
     private NotificationQueue subscriptionEventQueue;
     private final SubscriptionBaseApiService apiService;
+    private final NonEntityDao nonEntityDao;
+    private final CacheControllerDispatcher controllerDispatcher;
 
     @Inject
     public DefaultSubscriptionBaseService(final Clock clock, final SubscriptionDao dao, final PlanAligner planAligner,
-                                          final AddonUtils addonUtils, final PersistentBus eventBus,
+                                          final PersistentBus eventBus,
                                           final NotificationQueueService notificationQueueService,
                                           final InternalCallContextFactory internalCallContextFactory,
-                                          final SubscriptionBaseApiService apiService) {
+                                          final SubscriptionBaseApiService apiService,
+                                          final NonEntityDao nonEntityDao,
+                                          final CacheControllerDispatcher controllerDispatcher) {
         this.clock = clock;
         this.dao = dao;
         this.planAligner = planAligner;
-        this.addonUtils = addonUtils;
         this.eventBus = eventBus;
         this.notificationQueueService = notificationQueueService;
         this.internalCallContextFactory = internalCallContextFactory;
         this.apiService = apiService;
+        this.nonEntityDao = nonEntityDao;
+        this.controllerDispatcher = controllerDispatcher;
     }
 
     @Override
@@ -164,7 +175,8 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
         if (event.getType() == EventType.PHASE) {
             onPhaseEvent(subscription, context);
         } else if (event.getType() == EventType.API_USER && subscription.getCategory() == ProductCategory.BASE) {
-            theRealSeqId = onBasePlanEvent(subscription, (ApiEvent) event, context);
+            final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID));
+            theRealSeqId = onBasePlanEvent(subscription, (ApiEvent) event, context.toCallContext(tenantId));
         }
 
         try {
@@ -183,7 +195,8 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
             final DateTime now = clock.getUTCNow();
             final TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now, now);
             final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
-                                              PhaseEventData.createNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
+                                              PhaseEventData.createNextPhaseEvent(subscription.getId(), subscription.getActiveVersion(),
+                                                                                  nextTimedPhase.getPhase().getName(), now, nextTimedPhase.getStartPhase()) :
                                               null;
             if (nextPhaseEvent != null) {
                 dao.createNextPhaseEvent(subscription, nextPhaseEvent, context);
@@ -193,7 +206,8 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
         }
     }
 
-    private int onBasePlanEvent(final DefaultSubscriptionBase baseSubscription, final ApiEvent event, final InternalCallContext context) {
-        return apiService.cancelAddOnsIfRequired(baseSubscription, event.getEffectiveDate(), context);
+    private int onBasePlanEvent(final DefaultSubscriptionBase baseSubscription, final ApiEvent event, final CallContext context) {
+        final Product baseProduct = (baseSubscription.getState() == EntitlementState.CANCELLED) ? null : baseSubscription.getCurrentPlan().getProduct();
+        return apiService.cancelAddOnsIfRequired(baseProduct, baseSubscription.getBundleId(), event.getEffectiveDate(), context);
     }
 }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index ab83318..7aa2fd9 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -305,8 +305,8 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
     }
 
     @Override
-    public List<SubscriptionBase> getSubscriptions(final UUID bundleId, final InternalTenantContext context) {
-        return buildBundleSubscriptions(getSubscriptionFromBundleId(bundleId, context), null, context);
+    public List<SubscriptionBase> getSubscriptions(final UUID bundleId, final List<SubscriptionBaseEvent> dryRunEvents, final InternalTenantContext context) {
+        return buildBundleSubscriptions(getSubscriptionFromBundleId(bundleId, context), null, dryRunEvents, context);
     }
 
     private List<SubscriptionBase> getSubscriptionFromBundleId(final UUID bundleId, final InternalTenantContext context) {
@@ -353,7 +353,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
                 eventsForSubscriptions.putAll(cur.getId(), ImmutableList.copyOf(events));
             }
 
-            result.put(bundleId, buildBundleSubscriptions(subscriptionsForBundle, eventsForSubscriptions, context));
+            result.put(bundleId, buildBundleSubscriptions(subscriptionsForBundle, eventsForSubscriptions, null, context));
         }
         return result;
     }
@@ -830,7 +830,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
             bundleInput.add(input);
         }
 
-        final List<SubscriptionBase> reloadedSubscriptions = buildBundleSubscriptions(bundleInput, null, context);
+        final List<SubscriptionBase> reloadedSubscriptions = buildBundleSubscriptions(bundleInput, null, null, context);
         for (final SubscriptionBase cur : reloadedSubscriptions) {
             if (cur.getId().equals(input.getId())) {
                 return cur;
@@ -840,7 +840,9 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         throw new SubscriptionBaseError("Unexpected code path in buildSubscription");
     }
 
-    private List<SubscriptionBase> buildBundleSubscriptions(final List<SubscriptionBase> input, @Nullable final Multimap<UUID, SubscriptionBaseEvent> eventsForSubscription, final InternalTenantContext context) {
+
+    private List<SubscriptionBase> buildBundleSubscriptions(final List<SubscriptionBase> input, @Nullable final Multimap<UUID, SubscriptionBaseEvent> eventsForSubscription,
+                                                            @Nullable List<SubscriptionBaseEvent> dryRunEvents, final InternalTenantContext context) {
         if (input == null || input.size() == 0) {
             return Collections.emptyList();
         }
@@ -866,6 +868,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
             final List<SubscriptionBaseEvent> events = eventsForSubscription != null ?
                                                        (List<SubscriptionBaseEvent>) eventsForSubscription.get(cur.getId()) :
                                                        getEventsForSubscription(cur.getId(), context);
+            mergeDryRunEvents(cur.getId(), events, dryRunEvents);
 
             SubscriptionBase reloaded = createSubscriptionForInternalUse(cur, events);
 
@@ -919,6 +922,26 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         return result;
     }
 
+    private void mergeDryRunEvents(final UUID subscriptionId, final List<SubscriptionBaseEvent> events, @Nullable List<SubscriptionBaseEvent> dryRunEvents) {
+        if (dryRunEvents == null || dryRunEvents.isEmpty()) {
+            return;
+        }
+        for (SubscriptionBaseEvent curDryRun : dryRunEvents) {
+            if (curDryRun.getSubscriptionId() != null && curDryRun.getSubscriptionId().equals(subscriptionId)) {
+
+                //boolean inserted = false;
+                final Iterator<SubscriptionBaseEvent> it = events.iterator();
+                while (it.hasNext()) {
+                    final SubscriptionBaseEvent event = it.next();
+                    if (event.getEffectiveDate().isAfter(curDryRun.getEffectiveDate())) {
+                        it.remove();
+                    }
+                }
+                events.add(curDryRun);
+            }
+        }
+    }
+
     @Override
     public void migrate(final UUID accountId, final AccountMigrationData accountData, final InternalCallContext context) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/RepairSubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/RepairSubscriptionDao.java
index 0329399..91f3a2e 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/RepairSubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/RepairSubscriptionDao.java
@@ -272,7 +272,7 @@ public class RepairSubscriptionDao extends EntityDaoBase<SubscriptionBundleModel
     }
 
     @Override
-    public List<SubscriptionBase> getSubscriptions(final UUID bundleId, final InternalTenantContext context) {
+    public List<SubscriptionBase> getSubscriptions(final UUID bundleId, final List<SubscriptionBaseEvent> dryRunEvents, final InternalTenantContext context) {
         throw new SubscriptionBaseError(NOT_IMPLEMENTED);
     }
 
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
index 797c23d..45901e9 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
@@ -61,7 +61,7 @@ public interface SubscriptionDao extends EntityDao<SubscriptionBundleModelDao, S
     // SubscriptionBase retrieval
     public SubscriptionBase getBaseSubscription(UUID bundleId, InternalTenantContext context);
 
-    public List<SubscriptionBase> getSubscriptions(UUID bundleId, InternalTenantContext context);
+    public List<SubscriptionBase> getSubscriptions(UUID bundleId, List<SubscriptionBaseEvent> dryRunEvents, InternalTenantContext context);
 
     public Map<UUID, List<SubscriptionBase>> getSubscriptionsForAccount(InternalTenantContext context);
 
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/phase/PhaseEventData.java b/subscription/src/main/java/org/killbill/billing/subscription/events/phase/PhaseEventData.java
index 6af6942..b3da3d8 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/phase/PhaseEventData.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/phase/PhaseEventData.java
@@ -17,6 +17,8 @@
 package org.killbill.billing.subscription.events.phase;
 
 
+import java.util.UUID;
+
 import org.joda.time.DateTime;
 
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
@@ -56,15 +58,15 @@ public class PhaseEventData extends EventBase implements PhaseEvent {
                 + ", isActive()=" + isActive() + "]\n";
     }
 
-    public static PhaseEvent createNextPhaseEvent(final String phaseName, final DefaultSubscriptionBase subscription, final DateTime now, final DateTime effectiveDate) {
+    public static PhaseEvent createNextPhaseEvent(final UUID subscriptionId, final long activeVersion, final String phaseName,  final DateTime now, final DateTime effectiveDate) {
         return (phaseName == null) ?
                 null :
                 new PhaseEventData(new PhaseEventBuilder()
-                                           .setSubscriptionId(subscription.getId())
+                                           .setSubscriptionId(subscriptionId)
                                            .setRequestedDate(now)
                                            .setEffectiveDate(effectiveDate)
                                            .setProcessedDate(now)
-                                           .setActiveVersion(subscription.getActiveVersion())
+                                           .setActiveVersion(activeVersion)
                                            .setPhaseName(phaseName));
     }
 }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java b/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java
index 8e089ba..e29a18f 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java
@@ -243,7 +243,7 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
         final Plan plan = catalogService.getFullCatalog().findPlan(productName, clock.getUTCNow());
 
         // Same here for the requested date
-        final TimedPhase[] phases = planAligner.getCurrentAndNextTimedPhaseOnCreate(defaultSubscriptionBase, plan, initialPhase, priceList, clock.getUTCNow(), effectiveDate);
+        final TimedPhase[] phases = planAligner.getCurrentAndNextTimedPhaseOnCreate(defaultSubscriptionBase.getAlignStartDate(), defaultSubscriptionBase.getBundleStartDate(), plan, initialPhase, priceList, clock.getUTCNow(), effectiveDate);
         Assert.assertEquals(phases.length, 2);
 
         return phases;
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/migration/TestMigration.java b/subscription/src/test/java/org/killbill/billing/subscription/api/migration/TestMigration.java
index 22e1e04..727b555 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/migration/TestMigration.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/migration/TestMigration.java
@@ -20,8 +20,6 @@ import java.util.List;
 
 import org.joda.time.DateTime;
 import org.joda.time.Interval;
-import org.testng.annotations.Test;
-
 import org.killbill.billing.api.TestApiListener.NextEvent;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.PhaseType;
@@ -32,10 +30,12 @@ import org.killbill.billing.subscription.SubscriptionTestSuiteWithEmbeddedDB;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi.AccountMigration;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
+import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseTransition;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseTransitionData;
 import org.killbill.billing.subscription.events.user.ApiEventType;
+import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -45,7 +45,7 @@ import static org.testng.Assert.assertTrue;
 public class TestMigration extends SubscriptionTestSuiteWithEmbeddedDB {
 
     @Test(groups = "slow")
-    public void testSingleBasePlan() throws SubscriptionBaseMigrationApiException {
+    public void testSingleBasePlan() throws SubscriptionBaseMigrationApiException, SubscriptionBaseApiException {
         final DateTime startDate = clock.getUTCNow().minusMonths(2);
         final DateTime beforeMigration = clock.getUTCNow();
         final AccountMigration toBeMigrated = testUtil.createAccountForMigrationWithRegularBasePlan(startDate);
@@ -59,7 +59,7 @@ public class TestMigration extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(bundles.size(), 1);
         final SubscriptionBaseBundle bundle = bundles.get(0);
 
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), internalCallContext);
+        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), null, internalCallContext);
         assertEquals(subscriptions.size(), 1);
         final SubscriptionBase subscription = subscriptions.get(0);
         assertTrue(subscription.getStartDate().compareTo(startDate) == 0);
@@ -74,7 +74,7 @@ public class TestMigration extends SubscriptionTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testPlanWithAddOn() throws SubscriptionBaseMigrationApiException {
+    public void testPlanWithAddOn() throws SubscriptionBaseMigrationApiException, SubscriptionBaseApiException {
         final DateTime beforeMigration = clock.getUTCNow();
         final DateTime initalBPStart = clock.getUTCNow().minusMonths(3);
         final DateTime initalAddonStart = clock.getUTCNow().minusMonths(1).plusDays(7);
@@ -90,7 +90,7 @@ public class TestMigration extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(bundles.size(), 1);
         final SubscriptionBaseBundle bundle = bundles.get(0);
 
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), internalCallContext);
+        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), null, internalCallContext);
         assertEquals(subscriptions.size(), 2);
 
         final SubscriptionBase baseSubscription = (subscriptions.get(0).getCurrentPlan().getProduct().getCategory() == ProductCategory.BASE) ?
@@ -119,7 +119,7 @@ public class TestMigration extends SubscriptionTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testSingleBasePlanFutureCancelled() throws SubscriptionBaseMigrationApiException {
+    public void testSingleBasePlanFutureCancelled() throws SubscriptionBaseMigrationApiException, SubscriptionBaseApiException {
         final DateTime startDate = clock.getUTCNow().minusMonths(1);
         final DateTime beforeMigration = clock.getUTCNow();
         final AccountMigration toBeMigrated = testUtil.createAccountForMigrationWithRegularBasePlanFutreCancelled(startDate);
@@ -134,7 +134,7 @@ public class TestMigration extends SubscriptionTestSuiteWithEmbeddedDB {
         final SubscriptionBaseBundle bundle = bundles.get(0);
         //assertEquals(bundle.getStartDate(), effectiveDate);
 
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), internalCallContext);
+        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), null, internalCallContext);
         assertEquals(subscriptions.size(), 1);
         final SubscriptionBase subscription = subscriptions.get(0);
         assertTrue(subscription.getStartDate().compareTo(startDate) == 0);
@@ -163,7 +163,7 @@ public class TestMigration extends SubscriptionTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testSingleBasePlanWithPendingPhase() throws SubscriptionBaseMigrationApiException {
+    public void testSingleBasePlanWithPendingPhase() throws SubscriptionBaseMigrationApiException, SubscriptionBaseApiException {
         final DateTime trialDate = clock.getUTCNow().minusDays(10);
         final AccountMigration toBeMigrated = testUtil.createAccountForMigrationFuturePendingPhase(trialDate);
 
@@ -175,7 +175,7 @@ public class TestMigration extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(bundles.size(), 1);
         final SubscriptionBaseBundle bundle = bundles.get(0);
 
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), internalCallContext);
+        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), null, internalCallContext);
         assertEquals(subscriptions.size(), 1);
         final SubscriptionBase subscription = subscriptions.get(0);
 
@@ -206,7 +206,7 @@ public class TestMigration extends SubscriptionTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testSingleBasePlanWithPendingChange() throws SubscriptionBaseMigrationApiException {
+    public void testSingleBasePlanWithPendingChange() throws SubscriptionBaseMigrationApiException, SubscriptionBaseApiException {
         final DateTime beforeMigration = clock.getUTCNow();
         final AccountMigration toBeMigrated = testUtil.createAccountForMigrationFuturePendingChange();
         final DateTime afterMigration = clock.getUTCNow();
@@ -219,7 +219,7 @@ public class TestMigration extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(bundles.size(), 1);
         final SubscriptionBaseBundle bundle = bundles.get(0);
 
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), internalCallContext);
+        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), null, internalCallContext);
         assertEquals(subscriptions.size(), 1);
         final SubscriptionBase subscription = subscriptions.get(0);
         //assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
@@ -261,7 +261,7 @@ public class TestMigration extends SubscriptionTestSuiteWithEmbeddedDB {
         final List<SubscriptionBaseBundle> bundles = subscriptionInternalApi.getBundlesForAccount(toBeMigrated.getAccountKey(), internalCallContext);
         assertEquals(bundles.size(), 1);
 
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundles.get(0).getId(), internalCallContext);
+        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundles.get(0).getId(), null, internalCallContext);
         assertEquals(subscriptions.size(), 1);
         final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptions.get(0);
 
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java
index af2277c..4cc394d 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java
@@ -20,11 +20,6 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
 import org.killbill.billing.api.TestApiListener.NextEvent;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.PhaseType;
@@ -38,9 +33,12 @@ import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi.AccountMigration;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
@@ -68,7 +66,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
 
         final DateTime bundleCreatedDate = bundle.getCreatedDate();
 
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), internalCallContext);
+        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), null, internalCallContext);
         assertEquals(subscriptions.size(), 1);
         final SubscriptionBase subscription = subscriptions.get(0);
         testUtil.assertDateWithin(subscription.getStartDate(), beforeMigration.minusMonths(2), afterMigration.minusMonths(2));
@@ -137,7 +135,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(bundlesForAccountAndKey.size(), 1);
 
         final SubscriptionBaseBundle newBundle = bundlesForAccountAndKey.get(0);
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(newBundle.getId(), internalCallContext);
+        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(newBundle.getId(), null, internalCallContext);
         assertEquals(subscriptions.size(), 1);
 
         final SubscriptionBase newBaseSubscription = subscriptions.get(0);
@@ -187,7 +185,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
 
         final SubscriptionBaseBundle newBundle = bundlesForAccountAndKey.get(0);
 
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(newBundle.getId(), internalCallContext);
+        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(newBundle.getId(), null, internalCallContext);
         assertEquals(subscriptions.size(), 1);
 
         final SubscriptionBase newBaseSubscription = subscriptions.get(0);
@@ -237,7 +235,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(bundlesForAccountAndKey.size(), 1);
 
         final SubscriptionBaseBundle newBundle = bundlesForAccountAndKey.get(0);
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(newBundle.getId(), internalCallContext);
+        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(newBundle.getId(), null, internalCallContext);
         assertEquals(subscriptions.size(), 1);
 
         final SubscriptionBase newBaseSubscription = subscriptions.get(0);
@@ -286,7 +284,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(bundlesForAccountAndKey.size(), 1);
 
         final SubscriptionBaseBundle newBundle = bundlesForAccountAndKey.get(0);
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(newBundle.getId(), internalCallContext);
+        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(newBundle.getId(), null, internalCallContext);
         assertEquals(subscriptions.size(), 1);
 
         final SubscriptionBase newBaseSubscription = subscriptions.get(0);
@@ -379,7 +377,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(bundlesForAccountAndKey.size(), 1);
 
         final SubscriptionBaseBundle newBundle = bundlesForAccountAndKey.get(0);
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(newBundle.getId(), internalCallContext);
+        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(newBundle.getId(), null, internalCallContext);
         assertEquals(subscriptions.size(), 3);
         boolean foundBP = false;
         boolean foundAO1 = false;
@@ -472,7 +470,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(bundlesForAccountAndKey.size(), 1);
 
         final SubscriptionBaseBundle newBundle = bundlesForAccountAndKey.get(0);
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(newBundle.getId(), internalCallContext);
+        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(newBundle.getId(), null, internalCallContext);
         assertEquals(subscriptions.size(), 1);
     }
 
@@ -516,7 +514,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(bundlesForAccountAndKey.size(), 1);
 
         final SubscriptionBaseBundle newBundle = bundlesForAccountAndKey.get(0);
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(newBundle.getId(), internalCallContext);
+        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(newBundle.getId(), null, internalCallContext);
         assertEquals(subscriptions.size(), 1);
     }
 }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
index a52cd25..ce52349 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
@@ -216,7 +216,7 @@ public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBun
     }
 
     @Override
-    public List<SubscriptionBase> getSubscriptions(final UUID bundleId, final InternalTenantContext context) {
+    public List<SubscriptionBase> getSubscriptions(final UUID bundleId, final List<SubscriptionBaseEvent> dryRunEvents, final InternalTenantContext context) {
         final List<SubscriptionBase> results = new ArrayList<SubscriptionBase>();
         for (final SubscriptionBase cur : subscriptions) {
             if (cur.getBundleId().equals(bundleId)) {
diff --git a/util/src/test/java/org/killbill/billing/util/export/dao/TestDatabaseExportDao.java b/util/src/test/java/org/killbill/billing/util/export/dao/TestDatabaseExportDao.java
index 0494ed5..5abfe1a 100644
--- a/util/src/test/java/org/killbill/billing/util/export/dao/TestDatabaseExportDao.java
+++ b/util/src/test/java/org/killbill/billing/util/export/dao/TestDatabaseExportDao.java
@@ -80,7 +80,6 @@ public class TestDatabaseExportDao extends UtilTestSuiteWithEmbeddedDB {
 
         // Verify new dump
         final String newDump = getDump();
-        // Note: unclear why Jackson would quote the header?
         Assert.assertEquals(newDump, "-- accounts record_id,id,external_key,email,name,first_name_length,currency,billing_cycle_day_local,billing_cycle_day_utc,payment_method_id,time_zone,locale,address1,address2,company_name,city,state_or_province,country,postal_code,phone,migrated,is_notified_for_invoices,created_date,created_by,updated_date,updated_by,tenant_record_id\n" +
                                      String.format("%s,\"%s\",,%s,%s,%s,,,,,,,,,,,,,,,false,%s,\"%s\",%s,\"%s\",%s,%s", internalCallContext.getAccountRecordId(), accountId, accountEmail, accountName, firstNameLength,
                                                    isNotifiedForInvoices, "1970-05-24T18:33:02.000+0000", createdBy, "1982-02-18T20:03:42.000+0000", updatedBy, internalCallContext.getTenantRecordId()) + "\n" +