killbill-memoizeit

Details

diff --git a/api/src/main/java/com/ning/billing/catalog/api/Plan.java b/api/src/main/java/com/ning/billing/catalog/api/Plan.java
index 8518b94..551a9e1 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/Plan.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/Plan.java
@@ -45,6 +45,6 @@ public interface Plan {
 
     public abstract PlanPhase findPhase(String name) throws CatalogApiException;
 
-    public abstract DateTime dateOfFirstRecurringNonZeroCharge(DateTime subscriptionStartDate);
+    public abstract DateTime dateOfFirstRecurringNonZeroCharge(DateTime subscriptionStartDate, PhaseType intialPhaseType);
 
 }
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApi.java b/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApi.java
index 796717a..06c08ae 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/transfer/EntitlementTransferApi.java
@@ -24,7 +24,8 @@ import com.ning.billing.util.callcontext.CallContext;
 
 public interface EntitlementTransferApi {
 
-    public SubscriptionBundle transferBundle(final UUID sourceAccountId, final UUID destAccountId, final String bundleKey, final DateTime requestedDate, final boolean transferAddOn, final CallContext context)
+    public SubscriptionBundle transferBundle(final UUID sourceAccountId, final UUID destAccountId, final String bundleKey, final DateTime requestedDate,
+            final boolean transferAddOn, final boolean cancelImmediately, final CallContext context)
         throws EntitlementTransferApiException;
 
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBundleTransfer.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBundleTransfer.java
new file mode 100644
index 0000000..cdd35dd
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBundleTransfer.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.beatrix.integration;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+
+@Guice(modules = {BeatrixModule.class})
+public class TestBundleTransfer extends TestIntegrationBase {
+
+
+    @Test(groups = "slow")
+    public void testBundleTransferWithBPAnnualOnly() throws Exception {
+
+        final Account account = createAccountWithPaymentMethod(getAccountData(3));
+
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+
+        final DateTime initialDate = new DateTime(2012, 4, 1, 0, 15, 42, 0, testTimeZone);
+
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+        final SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "mycutebundle", context);
+
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.ANNUAL;
+        final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+        //
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        //
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE);
+        final PlanPhaseSpecifier bpPlanPhaseSpecifier = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null);
+        final SubscriptionData bpSubscription = subscriptionDataFromSubscription(entitlementUserApi.createSubscription(bundle.getId(),
+                                                                                                                       bpPlanPhaseSpecifier,
+                                                                                                                       null,
+                                                                                                                       context));
+        assertNotNull(bpSubscription);
+        assertTrue(busHandler.isCompleted(DELAY));
+        assertListenerStatus();
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId()).size(), 1);
+
+        assertEquals(entitlementUserApi.getSubscriptionFromId(bpSubscription.getId()).getCurrentPlan().getBillingPeriod(), BillingPeriod.ANNUAL);
+
+        // Move out of trials for interesting invoices adjustments
+        busHandler.pushExpectedEvent(NextEvent.PHASE);
+        busHandler.pushExpectedEvent(NextEvent.INVOICE);
+        busHandler.pushExpectedEvent(NextEvent.PAYMENT);
+        clock.addDays(40);
+        assertTrue(busHandler.isCompleted(DELAY));
+        assertListenerStatus();
+
+        // BUNDLE TRANSFER
+        final Account newAccount = createAccountWithPaymentMethod(getAccountData(17));
+
+        busHandler.pushExpectedEvent(NextEvent.TRANSFER);
+        busHandler.pushExpectedEvent(NextEvent.INVOICE);
+        busHandler.pushExpectedEvent(NextEvent.PAYMENT);
+        transferApi.transferBundle(account.getId(), newAccount.getId(), "mycutebundle", clock.getUTCNow(), false, false, context);
+        assertTrue(busHandler.isCompleted(DELAY));
+        assertListenerStatus();
+
+        List<Invoice> invoices =invoiceUserApi.getInvoicesByAccount(newAccount.getId());
+        assertEquals(invoices.size(), 1);
+
+        final List<InvoiceItem> invoiceItems = invoices.get(0).getInvoiceItems();
+        assertEquals(invoiceItems.size(), 1);
+        InvoiceItem theItem = invoiceItems.get(0);
+        Assert.assertTrue(theItem.getStartDate().compareTo(new LocalDate(2012,05,11)) == 0);
+        Assert.assertTrue(theItem.getEndDate().compareTo(new LocalDate(2013,05,11)) == 0);
+        Assert.assertTrue(theItem.getAmount().compareTo(new BigDecimal("2399.9500")) == 0);
+    }
+
+    @Test(groups = "slow")
+    public void testBundleTransferWithBPMonthlyOnly() throws Exception {
+
+        final Account account = createAccountWithPaymentMethod(getAccountData(9));
+
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+
+        final DateTime initialDate = new DateTime(2012, 4, 1, 0, 15, 42, 0, testTimeZone);
+
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+        final SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "mycutebundle", context);
+
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+        final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+        //
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        //
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE);
+        final PlanPhaseSpecifier bpPlanPhaseSpecifier = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null);
+        final SubscriptionData bpSubscription = subscriptionDataFromSubscription(entitlementUserApi.createSubscription(bundle.getId(),
+                                                                                                                       bpPlanPhaseSpecifier,
+                                                                                                                       null,
+                                                                                                                       context));
+        assertNotNull(bpSubscription);
+        assertTrue(busHandler.isCompleted(DELAY));
+        assertListenerStatus();
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId()).size(), 1);
+
+        assertEquals(entitlementUserApi.getSubscriptionFromId(bpSubscription.getId()).getCurrentPlan().getBillingPeriod(), BillingPeriod.MONTHLY);
+
+        // Move out of trials for interesting invoices adjustments
+        busHandler.pushExpectedEvent(NextEvent.PHASE);
+        busHandler.pushExpectedEvent(NextEvent.INVOICE);
+        busHandler.pushExpectedEvent(NextEvent.PAYMENT);
+        clock.addDays(32);
+        assertTrue(busHandler.isCompleted(DELAY));
+        assertListenerStatus();
+
+        // BUNDLE TRANSFER
+        final Account newAccount = createAccountWithPaymentMethod(getAccountData(15));
+
+        busHandler.pushExpectedEvent(NextEvent.TRANSFER);
+        busHandler.pushExpectedEvent(NextEvent.INVOICE);
+        busHandler.pushExpectedEvent(NextEvent.PAYMENT);
+        transferApi.transferBundle(account.getId(), newAccount.getId(), "mycutebundle", clock.getUTCNow(), false, false, context);
+        assertTrue(busHandler.isCompleted(DELAY));
+        assertListenerStatus();
+
+        List<Invoice> invoices =invoiceUserApi.getInvoicesByAccount(newAccount.getId());
+        assertEquals(invoices.size(), 1);
+
+        final List<InvoiceItem> invoiceItems = invoices.get(0).getInvoiceItems();
+        assertEquals(invoiceItems.size(), 1);
+        InvoiceItem theItem = invoiceItems.get(0);
+        Assert.assertTrue(theItem.getStartDate().compareTo(new LocalDate(2012,05,03)) == 0);
+        Assert.assertTrue(theItem.getEndDate().compareTo(new LocalDate(2012,05,15)) == 0);
+        Assert.assertTrue(theItem.getAmount().compareTo(new BigDecimal("99.98")) == 0);
+    }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
index 48607a0..4aa10d8 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
@@ -53,6 +53,7 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.entitlement.api.EntitlementService;
 import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
+import com.ning.billing.entitlement.api.transfer.EntitlementTransferApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
@@ -125,10 +126,14 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
 
     @Inject
     protected MysqlTestingHelper helper;
+
     @Inject
     protected EntitlementUserApi entitlementUserApi;
 
     @Inject
+    protected EntitlementTransferApi transferApi;
+
+    @Inject
     protected EntitlementTimelineApi repairApi;
 
     @Inject
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultPlan.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultPlan.java
index fcf35b5..efb16c9 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultPlan.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultPlan.java
@@ -35,6 +35,7 @@ import org.joda.time.DateTime;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.Product;
@@ -234,9 +235,17 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
     }
 
     @Override
-    public DateTime dateOfFirstRecurringNonZeroCharge(final DateTime subscriptionStartDate) {
+    public DateTime dateOfFirstRecurringNonZeroCharge(final DateTime subscriptionStartDate, final PhaseType initialPhaseType) {
         DateTime result = subscriptionStartDate.toDateTime();
+        boolean skipPhase = initialPhaseType == null ? false : true;
         for (final PlanPhase phase : getAllPhases()) {
+            if (skipPhase) {
+                if (phase.getPhaseType() != initialPhaseType) {
+                    continue;
+                } else {
+                    skipPhase = false;
+                }
+            }
             if (phase.getRecurringPrice() == null || phase.getRecurringPrice().isZero()) {
                 result = phase.getDuration().addToDateTime(result);
             } else {
diff --git a/catalog/src/test/java/com/ning/billing/catalog/TestPlan.java b/catalog/src/test/java/com/ning/billing/catalog/TestPlan.java
index cf2987e..3af91ed 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/TestPlan.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestPlan.java
@@ -50,8 +50,8 @@ public class TestPlan extends CatalogTestSuite {
         final DefaultPlan p2 = MockPlan.createBicycleNoTrialEvergreen1USD();
 
         final DateTime requestedDate = new DateTime();
-        Assert.assertEquals(p0.dateOfFirstRecurringNonZeroCharge(requestedDate).compareTo(requestedDate.plusDays(30)), 0);
-        Assert.assertEquals(p1.dateOfFirstRecurringNonZeroCharge(requestedDate).compareTo(requestedDate.plusDays(100)), 0);
-        Assert.assertEquals(p2.dateOfFirstRecurringNonZeroCharge(requestedDate).compareTo(requestedDate.plusDays(0)), 0);
+        Assert.assertEquals(p0.dateOfFirstRecurringNonZeroCharge(requestedDate, null).compareTo(requestedDate.plusDays(30)), 0);
+        Assert.assertEquals(p1.dateOfFirstRecurringNonZeroCharge(requestedDate, null).compareTo(requestedDate.plusDays(100)), 0);
+        Assert.assertEquals(p2.dateOfFirstRecurringNonZeroCharge(requestedDate, null).compareTo(requestedDate.plusDays(0)), 0);
     }
 }
diff --git a/catalog/src/test/java/com/ning/billing/mock/catalog/MockPlan.java b/catalog/src/test/java/com/ning/billing/mock/catalog/MockPlan.java
index 426c690..720243a 100644
--- a/catalog/src/test/java/com/ning/billing/mock/catalog/MockPlan.java
+++ b/catalog/src/test/java/com/ning/billing/mock/catalog/MockPlan.java
@@ -23,6 +23,7 @@ import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.Product;
@@ -86,7 +87,7 @@ public class MockPlan implements Plan {
 
     @Override
     public DateTime dateOfFirstRecurringNonZeroCharge(
-            final DateTime subscriptionStartDate) {
+            final DateTime subscriptionStartDate, PhaseType phaseType) {
         return null;
     }
 
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java
index 99155bd..85d8c31 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java
@@ -201,8 +201,7 @@ public class DefaultEntitlementTransferApi implements EntitlementTransferApi {
     @Override
     public SubscriptionBundle transferBundle(final UUID sourceAccountId, final UUID destAccountId,
             final String bundleKey, final DateTime transferDate, final boolean transferAddOn,
-            final CallContext context) throws EntitlementTransferApiException {
-
+            final boolean cancelImmediately, final CallContext context) throws EntitlementTransferApiException {
         try {
 
             final DateTime effectiveTransferDate = transferDate == null ? clock.getUTCNow() : transferDate;
@@ -235,7 +234,8 @@ public class DefaultEntitlementTransferApi implements EntitlementTransferApi {
                 } else {
 
                     // If BP or STANDALONE subscription, create the cancel event on effectiveCancelDate
-                    final DateTime effectiveCancelDate = oldSubscription.getChargedThroughDate() != null && effectiveTransferDate.isBefore(oldSubscription.getChargedThroughDate()) ?
+                    final DateTime effectiveCancelDate = !cancelImmediately && oldSubscription.getChargedThroughDate() != null &&
+                        effectiveTransferDate.isBefore(oldSubscription.getChargedThroughDate()) ?
                             oldSubscription.getChargedThroughDate() : effectiveTransferDate;
 
                             final EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java
index be36ccb..50798e6 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java
@@ -77,7 +77,7 @@ public class TestTransfer extends TestApiBase {
 
         testListener.pushExpectedEvent(NextEvent.TRANSFER);
         testListener.pushExpectedEvent(NextEvent.CANCEL);
-        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, context);
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, false, context);
         assertTrue(testListener.isCompleted(3000));
         final DateTime afterTransferDate = clock.getUTCNow();
 
@@ -128,7 +128,7 @@ public class TestTransfer extends TestApiBase {
 
         testListener.pushExpectedEvent(NextEvent.TRANSFER);
         final DateTime transferRequestedDate = clock.getUTCNow();
-        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, context);
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, false, context);
         assertTrue(testListener.isCompleted(3000));
 
         // CHECK OLD BASE IS CANCEL AT THE TRANSFER DATE
@@ -176,7 +176,7 @@ public class TestTransfer extends TestApiBase {
         final DateTime transferRequestedDate = clock.getUTCNow();
         testListener.pushExpectedEvent(NextEvent.TRANSFER);
         testListener.pushExpectedEvent(NextEvent.CANCEL);
-        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, context);
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, false, context);
         assertTrue(testListener.isCompleted(3000));
         final DateTime afterTransferDate = clock.getUTCNow();
 
@@ -227,7 +227,7 @@ public class TestTransfer extends TestApiBase {
 
         final DateTime transferRequestedDate = clock.getUTCNow();
         testListener.pushExpectedEvent(NextEvent.TRANSFER);
-        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, context);
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, false, context);
         assertTrue(testListener.isCompleted(3000));
 
         // CHECK OLD BASE IS CANCEL AT THE TRANSFER DATE
@@ -322,7 +322,7 @@ public class TestTransfer extends TestApiBase {
 
         final DateTime transferRequestedDate = clock.getUTCNow();
         testListener.pushExpectedEvent(NextEvent.TRANSFER);
-        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, true, context);
+        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, true, false, context);
         assertTrue(testListener.isCompleted(3000));
 
         // RETRIEVE NEW BUNDLE AND CHECK SUBSCRIPTIONS
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
index 083a9a8..3863737 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
@@ -175,6 +175,7 @@ public class BundleResource extends JaxRsResourceBase {
     public Response transferBundle(@PathParam(ID_PARAM_NAME) final String id,
             @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
             @QueryParam(QUERY_BUNDLE_TRANSFER_ADDON) @DefaultValue("true") final Boolean transferAddOn,
+            @QueryParam(QUERY_BUNDLE_TRANSFER_CANCEL_IMM) @DefaultValue("false") final Boolean cancelImmediatley,
             final BundleJsonNoSubscriptions json,
             @HeaderParam(HDR_CREATED_BY) final String createdBy,
             @HeaderParam(HDR_REASON) final String reason,
@@ -184,7 +185,7 @@ public class BundleResource extends JaxRsResourceBase {
         final SubscriptionBundle bundle = entitlementApi.getBundleFromId(UUID.fromString(id));
         final DateTime inputDate = (requestedDate != null) ? DATE_TIME_FORMATTER.parseDateTime(requestedDate) : null;
         final SubscriptionBundle newBundle = transferApi.transferBundle(bundle.getAccountId(), UUID.fromString(json.getAccountId()), bundle.getKey(), inputDate, transferAddOn,
-                context.createContext(createdBy, reason, comment));
+                cancelImmediatley, context.createContext(createdBy, reason, comment));
 
         return uriBuilder.buildResponse(BundleResource.class, "getBundle", newBundle.getId(), uriInfo.getBaseUri().toString());
     }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
index 4c9ceb0..037d036 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
@@ -62,6 +62,7 @@ public interface JaxrsResource {
     public static final String QUERY_PAYMENT_METHOD_IS_DEFAULT = "isDefault";
 
     public static final String QUERY_BUNDLE_TRANSFER_ADDON = "transferAddOn";
+    public static final String QUERY_BUNDLE_TRANSFER_CANCEL_IMM = "cancelImmediately";
 
     public static final String ACCOUNTS = "accounts";
     public static final String ACCOUNTS_PATH = PREFIX + "/" + ACCOUNTS;
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BillCycleDayCalculator.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BillCycleDayCalculator.java
index fb8998e..8ecc101 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BillCycleDayCalculator.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BillCycleDayCalculator.java
@@ -29,6 +29,7 @@ import com.ning.billing.catalog.api.BillingAlignment;
 import com.ning.billing.catalog.api.Catalog;
 import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
@@ -118,7 +119,9 @@ public class BillCycleDayCalculator {
 
     @VisibleForTesting
     BillCycleDay calculateBcdFromSubscription(final Subscription subscription, final Plan plan, final Account account) throws AccountApiException {
-        final DateTime date = plan.dateOfFirstRecurringNonZeroCharge(subscription.getStartDate());
+
+        final PhaseType currentPhaseType = subscription.getCurrentPhase() != null ?  subscription.getCurrentPhase().getPhaseType() : null;
+        final DateTime date = plan.dateOfFirstRecurringNonZeroCharge(subscription.getStartDate(), currentPhaseType);
         // There are really two kinds of billCycleDay:
         // - a System billingCycleDay which should be computed from UTC time (in order to get the correct notification time at
         //   the end of each service period)
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillCycleDayCalculator.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillCycleDayCalculator.java
index b5b327a..f243bbf 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillCycleDayCalculator.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillCycleDayCalculator.java
@@ -60,7 +60,7 @@ public class TestBillCycleDayCalculator {
 
         // Create a the base plan associated with that subscription
         final Plan plan = Mockito.mock(Plan.class);
-        Mockito.when(plan.dateOfFirstRecurringNonZeroCharge(bpStartDateUTC)).thenReturn(bpStartDateUTC);
+        Mockito.when(plan.dateOfFirstRecurringNonZeroCharge(bpStartDateUTC, null)).thenReturn(bpStartDateUTC);
         final Catalog catalog = Mockito.mock(Catalog.class);
         Mockito.when(catalog.findPlan(Mockito.anyString(), Mockito.<DateTime>any(), Mockito.<DateTime>any())).thenReturn(plan);
 
@@ -140,7 +140,7 @@ public class TestBillCycleDayCalculator {
         Mockito.when(subscription.getStartDate()).thenReturn(startDateUTC);
 
         final Plan plan = Mockito.mock(Plan.class);
-        Mockito.when(plan.dateOfFirstRecurringNonZeroCharge(startDateUTC)).thenReturn(startDateUTC);
+        Mockito.when(plan.dateOfFirstRecurringNonZeroCharge(startDateUTC, null)).thenReturn(startDateUTC);
 
         final Account account = Mockito.mock(Account.class);
         Mockito.when(account.getTimeZone()).thenReturn(accountTimeZone);
diff --git a/util/src/test/java/com/ning/billing/mock/MockPlan.java b/util/src/test/java/com/ning/billing/mock/MockPlan.java
index 2e153c8..2d39621 100644
--- a/util/src/test/java/com/ning/billing/mock/MockPlan.java
+++ b/util/src/test/java/com/ning/billing/mock/MockPlan.java
@@ -24,6 +24,7 @@ import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.Product;
@@ -97,7 +98,7 @@ public class MockPlan implements Plan {
     }
 
     @Override
-    public DateTime dateOfFirstRecurringNonZeroCharge(final DateTime subscriptionStartDate) {
+    public DateTime dateOfFirstRecurringNonZeroCharge(final DateTime subscriptionStartDate, PhaseType initialPhaseType) {
         throw new UnsupportedOperationException();
     }
 }