killbill-uncached

Entitlement/subscriptionBase changes to address the following

9/7/2013 9:33:56 PM

Changes

pom.xml 2(+1 -1)

Details

diff --git a/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBase.java b/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBase.java
index d7856dc..40a66c3 100644
--- a/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBase.java
+++ b/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBase.java
@@ -41,19 +41,25 @@ import com.ning.billing.util.entity.Entity;
 public interface SubscriptionBase extends Entity, Blockable {
 
 
-    public boolean cancel(final DateTime requestedDate, final CallContext context)
+    public boolean cancel(final CallContext context)
             throws SubscriptionBaseApiException;
 
-    public boolean cancelWithPolicy(final DateTime requestedDate, final BillingActionPolicy policy, final CallContext context)
+    public boolean cancelWithDate(final DateTime requestedDate, final CallContext context)
+            throws SubscriptionBaseApiException;
+
+    public boolean cancelWithPolicy(final BillingActionPolicy policy, final CallContext context)
             throws SubscriptionBaseApiException;
 
     public boolean uncancel(final CallContext context)
             throws SubscriptionBaseApiException;
 
-    public boolean changePlan(final String productName, final BillingPeriod term, final String priceList, final DateTime requestedDate, final CallContext context)
+    public boolean changePlan(final String productName, final BillingPeriod term, final String priceList, final CallContext context)
+            throws SubscriptionBaseApiException;
+
+    public boolean changePlanWithDate(final String productName, final BillingPeriod term, final String priceList, final DateTime requestedDate, final CallContext context)
             throws SubscriptionBaseApiException;
 
-    public boolean changePlanWithPolicy(final String productName, final BillingPeriod term, final String priceList, final DateTime requestedDate,
+    public boolean changePlanWithPolicy(final String productName, final BillingPeriod term, final String priceList,
                                         final BillingActionPolicy policy, final CallContext context)
             throws SubscriptionBaseApiException;
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
index c93235b..d58c481 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
@@ -36,13 +36,17 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.DefaultEntitlement;
+import com.ning.billing.entitlement.api.Entitlement;
+import com.ning.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
 import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
+import com.ning.billing.entitlement.api.SubscriptionBundle;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.payment.api.PaymentStatus;
 import com.ning.billing.subscription.api.user.DefaultSubscriptionBase;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
@@ -452,6 +456,42 @@ public class TestIntegration extends TestIntegrationBase {
 
 
     @Test(groups = "slow")
+    public void testCreateMultipleBPWithSameExternalKey() throws Exception {
+
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 13, 42, 0, testTimeZone);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(25));
+        assertNotNull(account);
+
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final SubscriptionBundle initialBundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey("bundleKey", callContext);
+
+        busHandler.pushExpectedEvent(NextEvent.CANCEL);
+        baseEntitlement.cancelEntitlementWithPolicy(EntitlementActionPolicy.IMMEDIATE, callContext);
+        assertTrue(busHandler.isCompleted(DELAY));
+
+        final String newProductName = "Pistol";
+        final DefaultEntitlement newBaseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", newProductName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+
+        final SubscriptionBundle newBundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey("bundleKey", callContext);
+
+        assertNotEquals(initialBundle.getId(), newBundle.getId());
+        assertEquals(initialBundle.getAccountId(), newBundle.getAccountId());
+        assertEquals(initialBundle.getExternalKey(), newBundle.getExternalKey());
+
+        final Entitlement refreshedBseEntitlement = entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
+
+        assertEquals(refreshedBseEntitlement.getState(), EntitlementState.CANCELLED);
+        assertEquals(newBaseEntitlement.getState(), EntitlementState.ACTIVE);
+    }
+
+
+
+        @Test(groups = "slow")
     public void testWithPauseResume() throws Exception {
         final DateTime initialDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
         final int billingDay = 2;
diff --git a/entitlement/killbill-entitlement.iml b/entitlement/killbill-entitlement.iml
index e359fc1..b894afd 100644
--- a/entitlement/killbill-entitlement.iml
+++ b/entitlement/killbill-entitlement.iml
@@ -36,7 +36,7 @@
     <orderEntry type="module" module-name="killbill-account" />
     <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.1.0" level="project" />
     <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.1.0" level="project" />
-    <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.6.2" level="project" />
+    <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.6.3-SNAPSHOT" level="project" />
     <orderEntry type="library" name="Maven: joda-time:joda-time:2.0" level="project" />
     <orderEntry type="module" module-name="killbill-internal-api" />
     <orderEntry type="library" name="Maven: com.ning.billing.plugin:killbill-plugin-api-payment:0.4.0" level="project" />
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
index d1249a0..861fc91 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
@@ -224,7 +224,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(accountId, callContext);
         final DateTime effectiveCancelDate = dateHelper.fromLocalDateAndReferenceTime(localCancelDate, subscriptionBase.getStartDate(), contextWithValidAccountRecordId);
         try {
-            subscriptionBase.cancel(null, callContext);
+            subscriptionBase.cancel(callContext);
             blockingStateDao.setBlockingState(new DefaultBlockingState(getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, effectiveCancelDate), clock, contextWithValidAccountRecordId);
             return entitlementApi.getEntitlementForId(getId(), callContext);
         } catch (SubscriptionBaseApiException e) {
@@ -249,7 +249,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         final LocalDate effectiveLocalDate = new LocalDate(localCancelDate, accountTimeZone);
         final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(effectiveLocalDate, subscriptionBase.getStartDate(), contextWithValidAccountRecordId);
         try {
-            subscriptionBase.cancelWithPolicy(null, billingPolicy, callContext);
+            subscriptionBase.cancelWithPolicy(billingPolicy, callContext);
             blockingStateDao.setBlockingState(new DefaultBlockingState(getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, effectiveDate), clock, contextWithValidAccountRecordId);
             return entitlementApi.getEntitlementForId(getId(), callContext);
         } catch (SubscriptionBaseApiException e) {
@@ -281,10 +281,9 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         }
 
         final InternalCallContext context = internalCallContextFactory.createInternalCallContext(accountId, callContext);
-        final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(localDate, subscriptionBase.getStartDate(), context);
-        try {
+         try {
             checker.checkBlockedChange(subscriptionBase, context);
-            subscriptionBase.changePlan(productName, billingPeriod, priceList, requestedDate, callContext);
+            subscriptionBase.changePlan(productName, billingPeriod, priceList, callContext);
             return entitlementApi.getEntitlementForId(getId(), callContext);
         } catch (BlockingApiException e) {
             throw new EntitlementApiException(e, e.getCode(), e.getMessage());
@@ -301,10 +300,9 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         }
 
         final InternalCallContext context = internalCallContextFactory.createInternalCallContext(accountId, callContext);
-        final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(localDate, subscriptionBase.getStartDate(), context);
         try {
             checker.checkBlockedChange(subscriptionBase, context);
-            subscriptionBase.changePlanWithPolicy(productName, billingPeriod, priceList, requestedDate, actionPolicy, callContext);
+            subscriptionBase.changePlanWithPolicy(productName, billingPeriod, priceList, actionPolicy, callContext);
             return entitlementApi.getEntitlementForId(getId(), callContext);
         } catch (BlockingApiException e) {
             throw new EntitlementApiException(e, e.getCode(), e.getMessage());
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
index 2510a62..34740e1 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -122,17 +122,21 @@ public class DefaultSubscriptionApi implements SubscriptionApi  {
     }
 
     @Override
-    public List<SubscriptionBundle> getSubscriptionBundlesForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
+    public SubscriptionBundle getActiveSubscriptionBundleForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
 
         final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
-        final List<SubscriptionBaseBundle> bundles = subscriptionInternalApi.getBundlesForKey(externalKey, internalContext);
+        try {
+            final SubscriptionBaseBundle baseBundle = subscriptionInternalApi.getActiveBundleForKey(externalKey, internalContext);
+            final List<Entitlement> allEntitlementsForBundle = entitlementApi.getAllEntitlementsForBundle(baseBundle.getId(), context);
 
-        final  List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(bundles.size());
-        for (SubscriptionBaseBundle cur : bundles) {
-            final SubscriptionBundle bundle = getSubscriptionBundleForAccountIdAndExternalKey(cur.getAccountId(), cur.getExternalKey(), context);
-            result.add(bundle);
+            return getSubscriptionBundleFromEntitlements(baseBundle.getId(), allEntitlementsForBundle, context);
+        } catch (SubscriptionBaseApiException e) {
+            throw new SubscriptionApiException(e);
+        } catch (EntitlementApiException e) {
+            throw new SubscriptionApiException(e);
+        } catch (AccountApiException e) {
+            throw new SubscriptionApiException(e);
         }
-        return result;
     }
 
     @Override
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 f6cefc7..a2a2b23 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
@@ -104,6 +104,17 @@ public class BundleResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
+
+    @GET
+    @Produces(APPLICATION_JSON)
+    public Response getBundleByKey(@QueryParam(QUERY_EXTERNAL_KEY) final String externalKey,
+                              @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException {
+        final SubscriptionBundle bundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey(externalKey, context.createContext(request));
+        final BundleJsonNoSubscriptions json = new BundleJsonNoSubscriptions(bundle);
+        return Response.status(Status.OK).entity(json).build();
+    }
+
+
     @GET
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + SUBSCRIPTIONS)
     @Produces(APPLICATION_JSON)

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 3a16c46..22ed688 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.4.5</version>
+        <version>0.4.6</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.6.6-SNAPSHOT</version>
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java b/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
index 278771c..76e38a3 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
@@ -38,6 +38,9 @@ import com.ning.http.client.Response;
 
 import com.fasterxml.jackson.core.type.TypeReference;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotEquals;
+
 public class TestBundle extends TestJaxrsBase {
 
     @Test(groups = "slow", enabled = true)
@@ -122,12 +125,23 @@ public class TestBundle extends TestJaxrsBase {
 
         final EntitlementJsonNoEvents entitlementJsonNoEvents = createEntitlement(accountJson.getAccountId(), "93199", productName, ProductCategory.BASE.toString(), term.toString(), true);
 
+
+        Map<String, String> queryParams = new HashMap<String, String>();
+        queryParams.put(JaxrsResource.QUERY_EXTERNAL_KEY, "93199");
+        String uri = JaxrsResource.BUNDLES_PATH;
+        Response response = doGet(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        final BundleJsonNoSubscriptions originalBundle = mapper.readValue(response.getResponseBody(), BundleJsonNoSubscriptions.class);
+        assertEquals(originalBundle.getAccountId(), accountJson.getAccountId());
+        assertEquals(originalBundle.getExternalKey(), "93199");
+
+
         final AccountJson newAccount = createAccountWithDefaultPaymentMethod("dst", "dst", "dst@yahoo.com");
 
         final BundleJsonNoSubscriptions newBundleInput = new BundleJsonNoSubscriptions(null, newAccount.getAccountId(), null, null, null);
         final String newBundleInputJson = mapper.writeValueAsString(newBundleInput);
-        final String uri = JaxrsResource.BUNDLES_PATH + "/" + entitlementJsonNoEvents.getBundleId();
-        Response response = doPut(uri, newBundleInputJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        uri = JaxrsResource.BUNDLES_PATH + "/" + entitlementJsonNoEvents.getBundleId();
+        response = doPut(uri, newBundleInputJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
 
         final String locationCC = response.getHeader("Location");
@@ -135,5 +149,17 @@ public class TestBundle extends TestJaxrsBase {
 
         response = doGetWithUrl(locationCC, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+
+        queryParams = new HashMap<String, String>();
+        queryParams.put(JaxrsResource.QUERY_EXTERNAL_KEY, "93199");
+        uri = JaxrsResource.BUNDLES_PATH;
+        response = doGet(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        final BundleJsonNoSubscriptions newBundle = mapper.readValue(response.getResponseBody(), BundleJsonNoSubscriptions.class);
+
+        assertNotEquals(newBundle.getBundleId(), originalBundle.getBundleId());
+        assertEquals(newBundle.getExternalKey(), originalBundle.getExternalKey());
+        assertEquals(newBundle.getAccountId(), newAccount.getAccountId());
+
     }
 }
diff --git a/subscription/killbill-subscription.iml b/subscription/killbill-subscription.iml
index 1063b5f..c1641a3 100644
--- a/subscription/killbill-subscription.iml
+++ b/subscription/killbill-subscription.iml
@@ -33,7 +33,7 @@
     <orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.1" level="project" />
     <orderEntry type="library" scope="TEST" name="Maven: cglib:cglib-nodep:2.2" level="project" />
     <orderEntry type="library" scope="TEST" name="Maven: org.objenesis:objenesis:1.2" level="project" />
-    <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.6.2" level="project" />
+    <orderEntry type="library" name="Maven: com.ning.billing:killbill-api:0.6.3-SNAPSHOT" level="project" />
     <orderEntry type="library" name="Maven: joda-time:joda-time:2.0" level="project" />
     <orderEntry type="module" module-name="killbill-catalog" scope="TEST" production-on-test="" />
     <orderEntry type="library" scope="TEST" name="Maven: com.ning.billing:killbill-catalog:test-jar:tests:0.6.6-SNAPSHOT" level="project" />
diff --git a/subscription/src/main/java/com/ning/billing/subscription/alignment/PlanAligner.java b/subscription/src/main/java/com/ning/billing/subscription/alignment/PlanAligner.java
index 23faac4..971b920 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/alignment/PlanAligner.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/alignment/PlanAligner.java
@@ -163,7 +163,7 @@ public class PlanAligner extends BaseAligner {
                                                                                lastPlanTransition.getNextPriceList().getName(),
                                                                                requestedDate);
                     return getTimedPhase(timedPhases, effectiveDate, WhichPhase.NEXT);
-                // If we went through Plan changes, borrow the logic for changePlan alignment
+                // If we went through Plan changes, borrow the logic for changePlanWithRequestedDate alignment
                 case CHANGE:
                     return getTimedPhaseOnChange(subscription.getAlignStartDate(),
                                                  subscription.getBundleStartDate(),
diff --git a/subscription/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseApiService.java b/subscription/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseApiService.java
index ff0e37b..41411b4 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseApiService.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseApiService.java
@@ -32,29 +32,36 @@ import com.ning.billing.util.callcontext.InternalCallContext;
 public interface SubscriptionBaseApiService {
 
     public DefaultSubscriptionBase createPlan(SubscriptionBuilder builder, Plan plan, PhaseType initialPhase,
-                                       String realPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate,
-                                       CallContext context)
+                                              String realPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate,
+                                              CallContext context)
             throws SubscriptionBaseApiException;
 
     @Deprecated
     public boolean recreatePlan(final DefaultSubscriptionBase subscription, final PlanPhaseSpecifier spec, final DateTime requestedDateWithMs, final CallContext context)
             throws SubscriptionBaseApiException;
 
-    public boolean cancel(DefaultSubscriptionBase subscription, DateTime requestedDate, CallContext context)
-        throws SubscriptionBaseApiException;
+    public boolean cancel(DefaultSubscriptionBase subscription, CallContext context)
+            throws SubscriptionBaseApiException;
+
+    public boolean cancelWithRequestedDate(DefaultSubscriptionBase subscription, DateTime requestedDate, CallContext context)
+            throws SubscriptionBaseApiException;
 
-    public boolean cancelWithPolicy(DefaultSubscriptionBase subscription, DateTime requestedDate, BillingActionPolicy policy, CallContext context)
-        throws SubscriptionBaseApiException;
+    public boolean cancelWithPolicy(DefaultSubscriptionBase subscription, BillingActionPolicy policy, CallContext context)
+            throws SubscriptionBaseApiException;
 
     public boolean uncancel(DefaultSubscriptionBase subscription, CallContext context)
             throws SubscriptionBaseApiException;
 
     public boolean changePlan(DefaultSubscriptionBase subscription, String productName, BillingPeriod term,
-                              String priceList, DateTime requestedDate, CallContext context)
+                              String priceList, CallContext context)
+            throws SubscriptionBaseApiException;
+
+    public boolean changePlanWithRequestedDate(DefaultSubscriptionBase subscription, String productName, BillingPeriod term,
+                                               String priceList, DateTime requestedDate, CallContext context)
             throws SubscriptionBaseApiException;
 
     public boolean changePlanWithPolicy(DefaultSubscriptionBase subscription, String productName, BillingPeriod term,
-                                        String priceList, DateTime requestedDate, BillingActionPolicy policy, CallContext context)
+                                        String priceList, BillingActionPolicy policy, CallContext context)
             throws SubscriptionBaseApiException;
 
     public int cancelAddOnsIfRequired(final DefaultSubscriptionBase baseSubscription, final DateTime effectiveDate, final InternalCallContext context);
diff --git a/subscription/src/main/java/com/ning/billing/subscription/api/timeline/SubscriptionDataRepair.java b/subscription/src/main/java/com/ning/billing/subscription/api/timeline/SubscriptionDataRepair.java
index a349beb..9ecbe2e 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/api/timeline/SubscriptionDataRepair.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/api/timeline/SubscriptionDataRepair.java
@@ -43,7 +43,6 @@ import com.ning.billing.subscription.events.SubscriptionBaseEvent;
 import com.ning.billing.subscription.events.SubscriptionBaseEvent.EventType;
 import com.ning.billing.subscription.events.user.ApiEventBuilder;
 import com.ning.billing.subscription.events.user.ApiEventCancel;
-import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.clock.Clock;
@@ -113,12 +112,12 @@ public class SubscriptionDataRepair extends DefaultSubscriptionBase {
                     checkAddonRights(baseSubscription);
                     break;
                 case CHANGE:
-                    changePlan(spec.getProductName(), spec.getBillingPeriod(), spec.getPriceListName(), input.getRequestedDate(), context);
+                    changePlanWithDate(spec.getProductName(), spec.getBillingPeriod(), spec.getPriceListName(), input.getRequestedDate(), context);
                     checkAddonRights(baseSubscription);
                     trickleDownBPEffectForAddon(addonSubscriptions, getLastUserEventEffectiveDate(), context);
                     break;
                 case CANCEL:
-                    cancel(input.getRequestedDate(), context);
+                    cancelWithDate(input.getRequestedDate(), context);
                     trickleDownBPEffectForAddon(addonSubscriptions, getLastUserEventEffectiveDate(), context);
                     break;
                 case PHASE:
diff --git a/subscription/src/main/java/com/ning/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java b/subscription/src/main/java/com/ning/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
index ef05793..25b6f2f 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
@@ -230,7 +230,7 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
                     }
                 } else {
 
-                    // If BP or STANDALONE subscription, create the cancel event on effectiveCancelDate
+                    // If BP or STANDALONE subscription, create the cancelWithRequestedDate event on effectiveCancelDate
                     final DateTime effectiveCancelDate = !cancelImmediately && oldSubscription.getChargedThroughDate() != null &&
                                                          effectiveTransferDate.isBefore(oldSubscription.getChargedThroughDate()) ?
                                                          oldSubscription.getChargedThroughDate() : effectiveTransferDate;
@@ -268,7 +268,7 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
             }
             BundleMigrationData bundleMigrationData = new BundleMigrationData(subscriptionBundleData, subscriptionMigrationDataList);
 
-            // Atomically cancel all subscription on old account and create new bundle, subscriptions, events for new account
+            // Atomically cancelWithRequestedDate all subscription on old account and create new bundle, subscriptions, events for new account
             dao.transfer(sourceAccountId, destAccountId, bundleMigrationData, transferCancelDataList, fromInternalCallContext, toInternalCallContext);
 
             return bundleMigrationData.getData();
diff --git a/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBase.java b/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBase.java
index 8041933..4c60d3d 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBase.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBase.java
@@ -81,7 +81,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
     private final DateTime paidThroughDate;
 
     //
-    // User APIs (create, change, cancel,...) will recompute those each time,
+    // User APIs (create, change, cancelWithRequestedDate,...) will recompute those each time,
     // so the user holding that subscription object get the correct state when
     // the call completes
     //
@@ -214,13 +214,18 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
     }
 
     @Override
-    public boolean cancel(final DateTime requestedDate, final CallContext context) throws SubscriptionBaseApiException {
-        return apiService.cancel(this, requestedDate, context);
+    public boolean cancel(final CallContext context) throws SubscriptionBaseApiException {
+        return apiService.cancel(this, context);
     }
 
     @Override
-    public boolean cancelWithPolicy(final DateTime requestedDate, final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
-        return apiService.cancelWithPolicy(this, requestedDate, policy, context);
+    public boolean cancelWithDate(final DateTime requestedDate, final CallContext context) throws SubscriptionBaseApiException {
+        return apiService.cancelWithRequestedDate(this, requestedDate, context);
+    }
+
+    @Override
+    public boolean cancelWithPolicy(final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
+        return apiService.cancelWithPolicy(this, policy, context);
     }
 
     @Override
@@ -231,14 +236,20 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
 
     @Override
     public boolean changePlan(final String productName, final BillingPeriod term, final String priceList,
-                              final DateTime requestedDate, final CallContext context) throws SubscriptionBaseApiException {
-        return apiService.changePlan(this, productName, term, priceList, requestedDate, context);
+                                      final CallContext context) throws SubscriptionBaseApiException {
+        return apiService.changePlan(this, productName, term, priceList, context);
+    }
+
+    @Override
+    public boolean changePlanWithDate(final String productName, final BillingPeriod term, final String priceList,
+                                      final DateTime requestedDate, final CallContext context) throws SubscriptionBaseApiException {
+        return apiService.changePlanWithRequestedDate(this, productName, term, priceList, requestedDate, context);
     }
 
     @Override
     public boolean changePlanWithPolicy(final String productName, final BillingPeriod term, final String priceList,
-                                        final DateTime requestedDate, final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
-        return apiService.changePlanWithPolicy(this, productName, term, priceList, requestedDate, policy, context);
+                                        final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
+        return apiService.changePlanWithPolicy(this, productName, term, priceList, policy, context);
     }
 
     @Override
@@ -474,24 +485,15 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         return getFutureEndDate() != null;
     }
 
-    public DateTime getPlanChangeEffectiveDate(final BillingActionPolicy policy,
-                                               final DateTime requestedDate) {
-
-        // Return requested date to potentially honor date in the past, or NOW
-        if (policy == BillingActionPolicy.IMMEDIATE) {
-            return requestedDate.compareTo(clock.getUTCNow()) < 0 ? requestedDate : clock.getUTCNow();
-        }
-
-        if (policy != BillingActionPolicy.END_OF_TERM) {
-            throw new SubscriptionBaseError(String.format(
-                    "Unexpected policy type %s", policy.toString()));
-        }
-
-        if (chargedThroughDate == null) {
-            return requestedDate;
-        } else {
-            return chargedThroughDate.isBefore(requestedDate) ? requestedDate
-                                                              : chargedThroughDate;
+    public DateTime getPlanChangeEffectiveDate(final BillingActionPolicy policy) {
+        switch (policy) {
+            case IMMEDIATE:
+                return clock.getUTCNow();
+            case END_OF_TERM:
+                return (chargedThroughDate != null) ? chargedThroughDate : clock.getUTCNow();
+            default:
+                throw new SubscriptionBaseError(String.format(
+                        "Unexpected policy type %s", policy.toString()));
         }
     }
 
diff --git a/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java b/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
index a9977e3..a795473 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
@@ -39,11 +39,13 @@ import com.ning.billing.catalog.api.PriceList;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.Product;
 import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.clock.Clock;
+import com.ning.billing.clock.DefaultClock;
 import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
 import com.ning.billing.subscription.alignment.PlanAligner;
 import com.ning.billing.subscription.alignment.TimedPhase;
-import com.ning.billing.subscription.api.SubscriptionBaseApiService;
 import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseApiService;
 import com.ning.billing.subscription.engine.addon.AddonUtils;
 import com.ning.billing.subscription.engine.dao.SubscriptionDao;
 import com.ning.billing.subscription.events.SubscriptionBaseEvent;
@@ -60,8 +62,6 @@ import com.ning.billing.subscription.exceptions.SubscriptionBaseError;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
-import com.ning.billing.clock.Clock;
-import com.ning.billing.clock.DefaultClock;
 
 import com.google.inject.Inject;
 
@@ -89,8 +89,8 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
     @Override
     public DefaultSubscriptionBase createPlan(final SubscriptionBuilder builder, final Plan plan, final PhaseType initialPhase,
-                                       final String realPriceList, final DateTime requestedDate, final DateTime effectiveDate, final DateTime processedDate,
-                                       final CallContext context) throws SubscriptionBaseApiException {
+                                              final String realPriceList, final DateTime requestedDate, final DateTime effectiveDate, final DateTime processedDate,
+                                              final CallContext context) throws SubscriptionBaseApiException {
         final DefaultSubscriptionBase subscription = new DefaultSubscriptionBase(builder, this, clock);
 
         createFromSubscription(subscription, plan, initialPhase, realPriceList, requestedDate, effectiveDate, processedDate, false, context);
@@ -116,7 +116,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
             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",
-                                                         spec.getProductName(), spec.getBillingPeriod().toString(), realPriceList));
+                                                              spec.getProductName(), spec.getBillingPeriod().toString(), realPriceList));
             }
 
             final DateTime effectiveDate = requestedDate;
@@ -170,53 +170,68 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
     }
 
     @Override
-    public boolean cancel(final DefaultSubscriptionBase subscription, final DateTime requestedDateWithMs, final CallContext context) throws SubscriptionBaseApiException {
-        try {
-            final EntitlementState currentState = subscription.getState();
-            if (currentState != EntitlementState.ACTIVE) {
-                throw new SubscriptionBaseApiException(ErrorCode.SUB_CANCEL_BAD_STATE, subscription.getId(), currentState);
-            }
-            final DateTime now = clock.getUTCNow();
-            final DateTime requestedDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
+    public boolean cancel(final DefaultSubscriptionBase subscription, final CallContext context) throws SubscriptionBaseApiException {
+
+        final EntitlementState currentState = subscription.getState();
+        if (currentState != EntitlementState.ACTIVE) {
+            throw new SubscriptionBaseApiException(ErrorCode.SUB_CANCEL_BAD_STATE, subscription.getId(), currentState);
+        }
+        final DateTime now = clock.getUTCNow();
+        final DateTime requestedDate = now;
 
-            final Plan currentPlan = subscription.getCurrentPlan();
-            final PlanPhaseSpecifier planPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
-                                                                        currentPlan.getProduct().getCategory(),
-                                                                        subscription.getCurrentPlan().getBillingPeriod(),
-                                                                        subscription.getCurrentPriceList().getName(),
-                                                                        subscription.getCurrentPhase().getPhaseType());
 
+        final Plan currentPlan = subscription.getCurrentPlan();
+        final PlanPhaseSpecifier planPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
+                                                                    currentPlan.getProduct().getCategory(),
+                                                                    subscription.getCurrentPlan().getBillingPeriod(),
+                                                                    subscription.getCurrentPriceList().getName(),
+                                                                    subscription.getCurrentPhase().getPhaseType());
+
+        try {
             final BillingActionPolicy policy = catalogService.getFullCatalog().planCancelPolicy(planPhase, requestedDate);
+            final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy);
 
-            return doCancelPlan(subscription, requestedDateWithMs, now, policy, context);
+            return doCancelPlan(subscription, now, requestedDate, effectiveDate, context);
         } catch (CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
     }
 
     @Override
-    public boolean cancelWithPolicy(final DefaultSubscriptionBase subscription, final DateTime requestedDateWithMs, final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
+    public boolean cancelWithRequestedDate(final DefaultSubscriptionBase subscription, final DateTime requestedDateWithMs, final CallContext context) throws SubscriptionBaseApiException {
         final EntitlementState currentState = subscription.getState();
         if (currentState != EntitlementState.ACTIVE) {
             throw new SubscriptionBaseApiException(ErrorCode.SUB_CANCEL_BAD_STATE, subscription.getId(), currentState);
         }
         final DateTime now = clock.getUTCNow();
-        return doCancelPlan(subscription, requestedDateWithMs, now, policy, context);
+        final DateTime requestedDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
+        return doCancelPlan(subscription, now, requestedDate, requestedDate, context);
     }
 
-    private boolean doCancelPlan(final DefaultSubscriptionBase subscription, final DateTime requestedDateWithMs, final DateTime now, final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
+    @Override
+    public boolean cancelWithPolicy(final DefaultSubscriptionBase subscription, final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
+        final EntitlementState currentState = subscription.getState();
+        if (currentState != EntitlementState.ACTIVE) {
+            throw new SubscriptionBaseApiException(ErrorCode.SUB_CANCEL_BAD_STATE, subscription.getId(), currentState);
+        }
+        final DateTime now = clock.getUTCNow();
+        final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy);
+
+        return doCancelPlan(subscription, now, now, effectiveDate, context);
+
+    }
+
+    private boolean doCancelPlan(final DefaultSubscriptionBase subscription, final DateTime now, final DateTime requestedDate, final DateTime effectiveDate, final CallContext context) throws SubscriptionBaseApiException {
 
-        final DateTime requestedDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
         validateRequestedDate(subscription, now, requestedDate);
-        final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, requestedDate);
 
         final SubscriptionBaseEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
-                                                                        .setSubscriptionId(subscription.getId())
-                                                                        .setActiveVersion(subscription.getActiveVersion())
-                                                                        .setProcessedDate(now)
-                                                                        .setEffectiveDate(effectiveDate)
-                                                                        .setRequestedDate(requestedDate)
-                                                                        .setFromDisk(true));
+                                                                             .setSubscriptionId(subscription.getId())
+                                                                             .setActiveVersion(subscription.getActiveVersion())
+                                                                             .setProcessedDate(now)
+                                                                             .setEffectiveDate(effectiveDate)
+                                                                             .setRequestedDate(requestedDate)
+                                                                             .setFromDisk(true));
 
         final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
         dao.cancelSubscription(subscription, cancelEvent, internalCallContext, 0);
@@ -224,7 +239,8 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
         cancelAddOnsIfRequired(subscription, effectiveDate, internalCallContext);
 
-        return (policy == BillingActionPolicy.IMMEDIATE);
+        final boolean isImmediate = subscription.getState() == EntitlementState.CANCELLED;
+        return isImmediate;
     }
 
     @Override
@@ -235,12 +251,12 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
         final DateTime now = clock.getUTCNow();
         final SubscriptionBaseEvent uncancelEvent = new ApiEventUncancel(new ApiEventBuilder()
-                                                                            .setSubscriptionId(subscription.getId())
-                                                                            .setActiveVersion(subscription.getActiveVersion())
-                                                                            .setProcessedDate(now)
-                                                                            .setRequestedDate(now)
-                                                                            .setEffectiveDate(now)
-                                                                            .setFromDisk(true));
+                                                                                 .setSubscriptionId(subscription.getId())
+                                                                                 .setActiveVersion(subscription.getActiveVersion())
+                                                                                 .setProcessedDate(now)
+                                                                                 .setRequestedDate(now)
+                                                                                 .setEffectiveDate(now)
+                                                                                 .setFromDisk(true));
 
         final List<SubscriptionBaseEvent> uncancelEvents = new ArrayList<SubscriptionBaseEvent>();
         uncancelEvents.add(uncancelEvent);
@@ -262,27 +278,27 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
     @Override
     public boolean changePlan(final DefaultSubscriptionBase subscription, final String productName, final BillingPeriod term,
-                              final String priceList, final DateTime requestedDateWithMs, final CallContext context)
+                                               final String priceList, final CallContext context)
             throws SubscriptionBaseApiException {
         final DateTime now = clock.getUTCNow();
-        final DateTime requestedDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
+        final DateTime requestedDate = now;
 
         validateRequestedDate(subscription, now, requestedDate);
         validateEntitlementState(subscription);
 
         final PlanChangeResult planChangeResult = getPlanChangeResult(subscription, productName, term, priceList, requestedDate);
-        final BillingActionPolicy policy = planChangeResult.getPolicy();
-
+        final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(planChangeResult.getPolicy());
         try {
-            return doChangePlan(subscription, planChangeResult, now, requestedDate, productName, term, policy, context);
+            return doChangePlan(subscription, productName, term, planChangeResult.getNewPriceList().getName(), now, requestedDate, effectiveDate, context);
         } catch (CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
     }
 
+
     @Override
-    public boolean changePlanWithPolicy(final DefaultSubscriptionBase subscription, final String productName, final BillingPeriod term,
-                                        final String priceList, final DateTime requestedDateWithMs, final BillingActionPolicy policy, final CallContext context)
+    public boolean changePlanWithRequestedDate(final DefaultSubscriptionBase subscription, final String productName, final BillingPeriod term,
+                                               final String priceList, final DateTime requestedDateWithMs, final CallContext context)
             throws SubscriptionBaseApiException {
         final DateTime now = clock.getUTCNow();
         final DateTime requestedDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
@@ -290,20 +306,35 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         validateRequestedDate(subscription, now, requestedDate);
         validateEntitlementState(subscription);
 
-        final PlanChangeResult planChangeResult = getPlanChangeResult(subscription, productName, term, priceList, requestedDate);
+        try {
+            return doChangePlan(subscription, productName, term, priceList, now, requestedDate, requestedDate, context);
+        } catch (CatalogApiException e) {
+            throw new SubscriptionBaseApiException(e);
+        }
+    }
 
+    @Override
+    public boolean changePlanWithPolicy(final DefaultSubscriptionBase subscription, final String productName, final BillingPeriod term,
+                                        final String priceList, final BillingActionPolicy policy, final CallContext context)
+            throws SubscriptionBaseApiException {
+        final DateTime now = clock.getUTCNow();
+        final DateTime requestedDate = now;
+
+        validateEntitlementState(subscription);
+
+        final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy);
         try {
-            return doChangePlan(subscription, planChangeResult, now, requestedDate, productName, term, policy, context);
+            return doChangePlan(subscription, productName, term, priceList, now, requestedDate, effectiveDate, context);
         } catch (CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
     }
 
     private PlanChangeResult getPlanChangeResult(final DefaultSubscriptionBase subscription, final String productName,
-                                                 final BillingPeriod term, final String priceList, final DateTime requestedDate) throws SubscriptionBaseApiException {
+                                                 final BillingPeriod term, final String priceList, final DateTime effectiveDate) throws SubscriptionBaseApiException {
         final PlanChangeResult planChangeResult;
         try {
-            final Product destProduct = catalogService.getFullCatalog().findProduct(productName, requestedDate);
+            final Product destProduct = catalogService.getFullCatalog().findProduct(productName, effectiveDate);
             final Plan currentPlan = subscription.getCurrentPlan();
             final PriceList currentPriceList = subscription.getCurrentPriceList();
             final PlanPhaseSpecifier fromPlanPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
@@ -316,7 +347,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                                                                 term,
                                                                 priceList);
 
-            planChangeResult = catalogService.getFullCatalog().planChange(fromPlanPhase, toPlanPhase, requestedDate);
+            planChangeResult = catalogService.getFullCatalog().planChange(fromPlanPhase, toPlanPhase, effectiveDate);
         } catch (CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
@@ -324,28 +355,31 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         return planChangeResult;
     }
 
-    private boolean doChangePlan(final DefaultSubscriptionBase subscription, final PlanChangeResult planChangeResult,
-                                 final DateTime now, final DateTime requestedDate, final String productName,
-                                 final BillingPeriod term, final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException, CatalogApiException {
-        final PriceList newPriceList = planChangeResult.getNewPriceList();
+    private boolean doChangePlan(final DefaultSubscriptionBase subscription,
+                                 final String newProductName,
+                                 final BillingPeriod newBillingPeriod,
+                                 final String newPriceList,
+                                 final DateTime now,
+                                 final DateTime requestedDate,
+                                 final DateTime effectiveDate,
+                                 final CallContext context) throws SubscriptionBaseApiException, CatalogApiException {
 
-        final Plan newPlan = catalogService.getFullCatalog().findPlan(productName, term, newPriceList.getName(), requestedDate, subscription.getStartDate());
-        final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, requestedDate);
+        final Plan newPlan = catalogService.getFullCatalog().findPlan(newProductName, newBillingPeriod, newPriceList, effectiveDate, subscription.getStartDate());
 
-        final TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), requestedDate, effectiveDate);
+        final TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnChange(subscription, newPlan, newPriceList, requestedDate, effectiveDate);
 
         final SubscriptionBaseEvent changeEvent = new ApiEventChange(new ApiEventBuilder()
-                                                                        .setSubscriptionId(subscription.getId())
-                                                                        .setEventPlan(newPlan.getName())
-                                                                        .setEventPlanPhase(currentTimedPhase.getPhase().getName())
-                                                                        .setEventPriceList(newPriceList.getName())
-                                                                        .setActiveVersion(subscription.getActiveVersion())
-                                                                        .setProcessedDate(now)
-                                                                        .setEffectiveDate(effectiveDate)
-                                                                        .setRequestedDate(requestedDate)
-                                                                        .setFromDisk(true));
-
-        final TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), requestedDate, effectiveDate);
+                                                                             .setSubscriptionId(subscription.getId())
+                                                                             .setEventPlan(newPlan.getName())
+                                                                             .setEventPlanPhase(currentTimedPhase.getPhase().getName())
+                                                                             .setEventPriceList(newPriceList)
+                                                                             .setActiveVersion(subscription.getActiveVersion())
+                                                                             .setProcessedDate(now)
+                                                                             .setEffectiveDate(effectiveDate)
+                                                                             .setRequestedDate(requestedDate)
+                                                                             .setFromDisk(true));
+
+        final TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList, requestedDate, effectiveDate);
         final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
                                           PhaseEventData.createNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
                                           null;
@@ -363,7 +397,9 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
         cancelAddOnsIfRequired(subscription, effectiveDate, internalCallContext);
 
-        return (policy == BillingActionPolicy.IMMEDIATE);
+        final boolean isChangeImmediate = subscription.getCurrentPlan().getProduct().getName().equals(newProductName) &&
+                                          subscription.getCurrentPlan().getBillingPeriod() == newBillingPeriod;
+        return isChangeImmediate;
     }
 
 
@@ -397,12 +433,12 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                 // Perform AO cancellation using the effectiveDate of the BP
                 //
                 final SubscriptionBaseEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
-                                                                                .setSubscriptionId(cur.getId())
-                                                                                .setActiveVersion(cur.getActiveVersion())
-                                                                                .setProcessedDate(now)
-                                                                                .setEffectiveDate(effectiveDate)
-                                                                                .setRequestedDate(now)
-                                                                                .setFromDisk(true));
+                                                                                     .setSubscriptionId(cur.getId())
+                                                                                     .setActiveVersion(cur.getActiveVersion())
+                                                                                     .setProcessedDate(now)
+                                                                                     .setEffectiveDate(effectiveDate)
+                                                                                     .setRequestedDate(now)
+                                                                                     .setFromDisk(true));
                 subscriptionsToBeCancelled.add(cur);
                 cancelEvents.add(cancelEvent);
             }
@@ -419,7 +455,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         final SubscriptionBaseTransition previousTransition = subscription.getPreviousTransition();
         if (previousTransition != null && previousTransition.getEffectiveTransitionTime().isAfter(requestedDate)) {
             throw new SubscriptionBaseApiException(ErrorCode.SUB_INVALID_REQUESTED_DATE,
-                                                  requestedDate.toString(), previousTransition.getEffectiveTransitionTime());
+                                                   requestedDate.toString(), previousTransition.getEffectiveTransitionTime());
         }
     }
 
@@ -433,6 +469,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         }
     }
 
+
     private InternalCallContext createCallContextFromBundleId(final UUID bundleId, final CallContext context) {
         return internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, context);
     }
diff --git a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 3b9dd95..decb58d 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -479,7 +479,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
                 for (final SubscriptionEventModelDao cur : eventModels) {
                     if (cur.getUserType() == ApiEventType.CANCEL) {
                         if (cancelledEvent != null) {
-                            throw new SubscriptionBaseError(String.format("Found multiple cancel active events for subscriptions %s", subscriptionId.toString()));
+                            throw new SubscriptionBaseError(String.format("Found multiple cancelWithRequestedDate active events for subscriptions %s", subscriptionId.toString()));
                         }
                         cancelledEvent = cur;
                     }
diff --git a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/SubscriptionDao.java b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/SubscriptionDao.java
index b1fd01f..9fd0210 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/SubscriptionDao.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/SubscriptionDao.java
@@ -71,7 +71,7 @@ public interface SubscriptionDao {
 
     public List<SubscriptionBaseEvent> getPendingEventsForSubscription(UUID subscriptionId, InternalTenantContext context);
 
-    // SubscriptionBase creation, cancellation, changePlan apis
+    // SubscriptionBase creation, cancellation, changePlanWithRequestedDate apis
     public void createSubscription(DefaultSubscriptionBase subscription, List<SubscriptionBaseEvent> initialEvents, InternalCallContext context);
 
     public void recreateSubscription(DefaultSubscriptionBase subscription, List<SubscriptionBaseEvent> recreateEvents, InternalCallContext context);
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/migration/TestMigration.java b/subscription/src/test/java/com/ning/billing/subscription/api/migration/TestMigration.java
index f9bc411..4512b71 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/migration/TestMigration.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/migration/TestMigration.java
@@ -304,7 +304,7 @@ public class TestMigration extends SubscriptionTestSuiteWithEmbeddedDB {
             assertEquals(billingTransitions.get(0), initialMigrateBilling);
 
             // Now make an IMMEDIATE change of plan
-            subscription.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), callContext);
+            subscription.changePlanWithDate("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), callContext);
 
             final List<SubscriptionBaseTransition> newTransitions = subscription.getAllTransitions();
             assertEquals(newTransitions.size(), 3);
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/timeline/TestRepairBP.java b/subscription/src/test/java/com/ning/billing/subscription/api/timeline/TestRepairBP.java
index a7d293d..2d7d120 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/timeline/TestRepairBP.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/timeline/TestRepairBP.java
@@ -581,8 +581,7 @@ public class TestRepairBP extends SubscriptionTestSuiteWithEmbeddedDB {
         subscriptionInternalApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate, internalCallContext);
         baseSubscription = subscriptionInternalApi.getSubscriptionFromId(baseSubscription.getId(), internalCallContext);
 
-        final DateTime requestedChange = clock.getUTCNow();
-        baseSubscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, requestedChange, callContext);
+        baseSubscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, callContext);
 
         // CHECK CHANGE DID NOT OCCUR YET
         Plan currentPlan = baseSubscription.getCurrentPlan();
@@ -655,7 +654,7 @@ public class TestRepairBP extends SubscriptionTestSuiteWithEmbeddedDB {
 
                 testListener.pushExpectedEvent(NextEvent.CHANGE);
                 final DateTime changeTime = clock.getUTCNow();
-                baseSubscription.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, changeTime, callContext);
+                baseSubscription.changePlanWithDate("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, changeTime, callContext);
                 assertTrue(testListener.isCompleted(5000));
 
                 repairApi.repairBundle(bRepair, true, callContext);
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/timeline/TestRepairWithAO.java b/subscription/src/test/java/com/ning/billing/subscription/api/timeline/TestRepairWithAO.java
index da293b0..8ea1dcb 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/timeline/TestRepairWithAO.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/timeline/TestRepairWithAO.java
@@ -375,7 +375,7 @@ public class TestRepairWithAO extends SubscriptionTestSuiteWithEmbeddedDB {
         expectedAO.add(testUtil.createExistingEventForAssertion(SubscriptionBaseTransitionType.PHASE, "Telescopic-Scope", PhaseType.EVERGREEN,
                                                                 ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusMonths(1)));
         expectedAO.add(testUtil.createExistingEventForAssertion(SubscriptionBaseTransitionType.CANCEL, "Telescopic-Scope", PhaseType.EVERGREEN,
-                                                                ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, newChargedThroughDate));
+                                                                ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, bpCancelDate));
 
         int index = 0;
         for (final ExistingEvent e : expectedAO) {
@@ -389,7 +389,7 @@ public class TestRepairWithAO extends SubscriptionTestSuiteWithEmbeddedDB {
         expectedBP.add(testUtil.createExistingEventForAssertion(SubscriptionBaseTransitionType.PHASE, "Shotgun", PhaseType.EVERGREEN,
                                                                 ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusDays(30)));
         expectedBP.add(testUtil.createExistingEventForAssertion(SubscriptionBaseTransitionType.CANCEL, "Shotgun", PhaseType.EVERGREEN,
-                                                                ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, newChargedThroughDate));
+                                                                ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, bpCancelDate));
         index = 0;
         for (final ExistingEvent e : expectedBP) {
             testUtil.validateExistingEventForAssertion(e, bpRepair.getExistingEvents().get(index++));
@@ -427,24 +427,6 @@ public class TestRepairWithAO extends SubscriptionTestSuiteWithEmbeddedDB {
         }
 
         newAoSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(aoSubscription.getId(), internalCallContext);
-        assertEquals(newAoSubscription.getState(), EntitlementState.ACTIVE);
-        assertEquals(newAoSubscription.getAllTransitions().size(), 3);
-        assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
-
-        newBaseSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(baseSubscription.getId(), internalCallContext);
-        assertEquals(newBaseSubscription.getState(), EntitlementState.ACTIVE);
-        assertEquals(newBaseSubscription.getAllTransitions().size(), 3);
-        assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
-
-        // MOVE CLOCK AFTER CANCEL DATE
-        testListener.pushExpectedEvent(NextEvent.CANCEL);
-        testListener.pushExpectedEvent(NextEvent.CANCEL);
-
-        it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(32));
-        clock.addDeltaFromReality(it.toDurationMillis());
-        assertTrue(testListener.isCompleted(7000));
-
-        newAoSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(aoSubscription.getId(), internalCallContext);
         assertEquals(newAoSubscription.getState(), EntitlementState.CANCELLED);
         assertEquals(newAoSubscription.getAllTransitions().size(), 3);
         assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
@@ -453,6 +435,7 @@ public class TestRepairWithAO extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(newBaseSubscription.getState(), EntitlementState.CANCELLED);
         assertEquals(newBaseSubscription.getAllTransitions().size(), 3);
         assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
     }
 
     @Test(groups = "slow")
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/timeline/TestRepairWithError.java b/subscription/src/test/java/com/ning/billing/subscription/api/timeline/TestRepairWithError.java
index cda8a39..688a9ba 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/timeline/TestRepairWithError.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/timeline/TestRepairWithError.java
@@ -99,7 +99,7 @@ public class TestRepairWithError extends SubscriptionTestSuiteNoDB {
 
                 testListener.pushExpectedEvent(NextEvent.CHANGE);
                 final DateTime changeTime = clock.getUTCNow();
-                baseSubscription.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, changeTime, callContext);
+                baseSubscription.changePlanWithDate("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, changeTime, callContext);
                 assertTrue(testListener.isCompleted(5000));
 
                 // MOVE AFTER TRIAL
@@ -390,7 +390,7 @@ public class TestRepairWithError extends SubscriptionTestSuiteNoDB {
                 assertTrue(testListener.isCompleted(5000));
 
                 DateTime requestedChange = clock.getUTCNow();
-                baseSubscription.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, requestedChange, callContext);
+                baseSubscription.changePlanWithRequestedDate("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, requestedChange, callContext);
 
                 DateTime reapairTime = clock.getUTCNow().minusDays(1);
 
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java b/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java
index ade6d63..941533e 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java
@@ -39,7 +39,6 @@ import com.ning.billing.subscription.api.user.DefaultSubscriptionBase;
 import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
 import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
 import com.ning.billing.subscription.api.SubscriptionBase;
-import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -312,7 +311,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
         final String newBaseProduct1 = "Assault-Rifle";
         final BillingPeriod newBaseTerm1 = BillingPeriod.ANNUAL;
         final DateTime changeDate1 = clock.getUTCNow();
-        newBaseSubscription.changePlan(newBaseProduct1, newBaseTerm1, basePriceList, changeDate1, callContext);
+        newBaseSubscription.changePlan(newBaseProduct1, newBaseTerm1, basePriceList, callContext);
 
         newPlan = newBaseSubscription.getCurrentPlan();
         assertEquals(newPlan.getProduct().getName(), newBaseProduct1);
@@ -328,7 +327,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
         final String newBaseProduct2 = "Pistol";
         final BillingPeriod newBaseTerm2 = BillingPeriod.ANNUAL;
         final DateTime changeDate2 = clock.getUTCNow();
-        newBaseSubscriptionWithCtd.changePlan(newBaseProduct2, newBaseTerm2, basePriceList, changeDate2, callContext);
+        newBaseSubscriptionWithCtd.changePlan(newBaseProduct2, newBaseTerm2, basePriceList, callContext);
 
         newPlan = newBaseSubscriptionWithCtd.getCurrentPlan();
         assertEquals(newPlan.getProduct().getName(), newBaseProduct1);
@@ -464,7 +463,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
         subscriptionInternalApi.setChargedThroughDate(aoSubscription1.getId(), ctd, internalCallContext);
 
         // CANCEL ADDON
-        aoSubscription1.cancel(clock.getUTCNow(), callContext);
+        aoSubscription1.cancelWithDate(clock.getUTCNow(), callContext);
 
         clock.addDays(1);
 
@@ -501,7 +500,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
 
         // CANCEL BP
         baseSubscription = subscriptionInternalApi.getSubscriptionFromId(baseSubscription.getId(), internalCallContext);
-        baseSubscription.cancel(clock.getUTCNow(), callContext);
+        baseSubscription.cancel(callContext);
 
         // MOVE CLOCK one day AHEAD AND UNCANCEL BP
         clock.addDays(1);
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiAddOn.java b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiAddOn.java
index a512244..8fe554e 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiAddOn.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiAddOn.java
@@ -66,7 +66,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
 
             testListener.pushExpectedEvent(NextEvent.CANCEL);
             final DateTime now = clock.getUTCNow();
-            aoSubscription.cancel(now, callContext);
+            aoSubscription.cancel(callContext);
 
             assertTrue(testListener.isCompleted(5000));
             aoSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(aoSubscription.getId(), internalCallContext);
@@ -120,13 +120,13 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
             clock.addDeltaFromReality(it.toDurationMillis());
 
             // CANCEL AO
-            aoSubscription.cancel(clock.getUTCNow(), callContext);
+            aoSubscription.cancel(callContext);
             aoSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(aoSubscription.getId(), internalCallContext);
             assertEquals(aoSubscription.getState(), EntitlementState.ACTIVE);
             assertTrue(aoSubscription.isSubscriptionFutureCancelled());
 
             // CANCEL BASE NOW
-            baseSubscription.cancel(clock.getUTCNow(), callContext);
+            baseSubscription.cancel(callContext);
             baseSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(baseSubscription.getId(), internalCallContext);
             assertEquals(baseSubscription.getState(), EntitlementState.ACTIVE);
             assertTrue(baseSubscription.isSubscriptionFutureCancelled());
@@ -194,7 +194,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
             baseSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(baseSubscription.getId(), internalCallContext);
 
             // FUTURE CANCELLATION
-            baseSubscription.cancel(now, callContext);
+            baseSubscription.cancel(callContext);
 
             // REFETCH AO SUBSCRIPTION AND CHECK THIS IS ACTIVE
             aoSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(aoSubscription.getId(), internalCallContext);
@@ -256,7 +256,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
             baseSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(baseSubscription.getId(), internalCallContext);
 
             // FUTURE CANCELLATION
-            baseSubscription.cancel(now, callContext);
+            baseSubscription.cancel(callContext);
 
             // REFETCH AO SUBSCRIPTION AND CHECK THIS IS ACTIVE
             aoSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(aoSubscription.getId(), internalCallContext);
@@ -279,7 +279,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
             clock.addDeltaFromReality(it.toDurationMillis());
 
             baseSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(baseSubscription.getId(), internalCallContext);
-            baseSubscription.cancel(clock.getUTCNow(), callContext);
+            baseSubscription.cancel(callContext);
             baseSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(baseSubscription.getId(), internalCallContext);
             assertEquals(baseSubscription.getState(), EntitlementState.ACTIVE);
             assertTrue(baseSubscription.isSubscriptionFutureCancelled());
@@ -347,7 +347,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
             testListener.reset();
             testListener.pushExpectedEvent(NextEvent.CHANGE);
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList, now, callContext);
+            baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList,  callContext);
             assertTrue(testListener.isCompleted(5000));
 
             // REFETCH AO SUBSCRIPTION AND CHECK THIS CANCELLED
@@ -407,7 +407,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
             assertEquals(aoStatus.get(0).getPriceList(), aoSubscription.getCurrentPriceList().getName());
             assertEquals(aoStatus.get(0).getReason(), DryRunChangeReason.AO_NOT_AVAILABLE_IN_NEW_PLAN);
 
-            baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList, now, callContext);
+            baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList, callContext);
 
             // REFETCH AO SUBSCRIPTION AND CHECK THIS IS ACTIVE
             aoSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(aoSubscription.getId(), internalCallContext);
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCancel.java b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCancel.java
index 1bcf3fb..9049f20 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCancel.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCancel.java
@@ -67,7 +67,7 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
 
 
             // CANCEL in trial period to get IMM policy
-            subscription.cancel(clock.getUTCNow(), callContext);
+            subscription.cancel(callContext);
             currentPhase = subscription.getCurrentPhase();
             testListener.isCompleted(3000);
 
@@ -125,7 +125,7 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
             // CANCEL
             testListener.setNonExpectedMode();
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            subscription.cancel(clock.getUTCNow(), callContext);
+            subscription.cancel(callContext);
             assertFalse(testListener.isCompleted(3000));
             testListener.reset();
 
@@ -191,7 +191,7 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
             testListener.pushExpectedEvent(NextEvent.CANCEL);
 
             // CANCEL
-            subscription.cancel(clock.getUTCNow(), callContext);
+            subscription.cancel(callContext);
             assertTrue(testListener.isCompleted(5000));
 
             final PlanPhase currentPhase = subscription.getCurrentPhase();
@@ -238,7 +238,7 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
             subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
 
             // CANCEL EOT
-            subscription.cancel(clock.getUTCNow(), callContext);
+            subscription.cancel(callContext);
 
             subscription.uncancel(callContext);
 
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiChangePlan.java b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiChangePlan.java
index a7601e7..02ec20d 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiChangePlan.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiChangePlan.java
@@ -83,7 +83,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
 
             // CHANGE PLAN
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), callContext);
+            subscription.changePlan(toProd, toTerm, toPlanSet, callContext);
             assertTrue(testListener.isCompleted(5000));
 
             // CHECK CHANGE PLAN
@@ -127,7 +127,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
             testListener.setNonExpectedMode();
             testListener.pushExpectedEvent(NextEvent.CHANGE);
             subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
-            subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), callContext);
+            subscription.changePlan(toProd, toTerm, toPlanSet, callContext);
             assertFalse(testListener.isCompleted(3000));
             testListener.reset();
 
@@ -176,7 +176,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
             clock.addDeltaFromReality(it.toDurationMillis());
 
             // CHANGE PLAN IMM
-            subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), callContext);
+            subscription.changePlan(toProd, toTerm, toPlanSet, callContext);
             checkChangePlan(subscription, toProd, ProductCategory.BASE, toTerm, PhaseType.TRIAL);
 
             assertTrue(testListener.isCompleted(5000));
@@ -236,7 +236,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
 
             // CHANGE PLAN
             currentTime = clock.getUTCNow();
-            subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), callContext);
+            subscription.changePlan(toProd, toTerm, toPlanSet, callContext);
 
             checkChangePlan(subscription, fromProd, ProductCategory.BASE, fromTerm, PhaseType.EVERGREEN);
 
@@ -314,13 +314,13 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
             // CHANGE EOT
             testListener.setNonExpectedMode();
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Pistol", BillingPeriod.MONTHLY, "gunclubDiscount", clock.getUTCNow(), callContext);
+            subscription.changePlan("Pistol", BillingPeriod.MONTHLY, "gunclubDiscount", callContext);
             assertFalse(testListener.isCompleted(3000));
             testListener.reset();
 
             // CHANGE
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), callContext);
+            subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", callContext);
             assertTrue(testListener.isCompleted(5000));
 
             final Plan currentPlan = subscription.getCurrentPlan();
@@ -363,14 +363,14 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
             // CHANGE EOT
             testListener.setNonExpectedMode();
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Shotgun", BillingPeriod.MONTHLY, "gunclubDiscount", clock.getUTCNow(), callContext);
+            subscription.changePlan("Shotgun", BillingPeriod.MONTHLY, "gunclubDiscount", callContext);
             assertFalse(testListener.isCompleted(3000));
             testListener.reset();
 
             // CHANGE EOT
             testListener.setNonExpectedMode();
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), callContext);
+            subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", callContext);
             assertFalse(testListener.isCompleted(3000));
             testListener.reset();
 
@@ -440,7 +440,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
             // CHANGE IMMEDIATE TO A 3 PHASES PLAN
             testListener.reset();
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), callContext);
+            subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", callContext);
             assertTrue(testListener.isCompleted(5000));
             testListener.reset();
 
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCreate.java b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCreate.java
index 293fa20..58a0b61 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCreate.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCreate.java
@@ -69,7 +69,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
                 Assert.assertEquals(e.getCode(), ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS.getCode());
             }
 
-            subscription.cancel(clock.getUTCNow(), callContext);
+            subscription.cancelWithDate(clock.getUTCNow(), callContext);
 
             final SubscriptionBaseBundle newBundle = subscriptionInternalApi.createBundleForAccount(bundle.getAccountId(), DefaultSubscriptionTestInitializer.DEFAULT_BUNDLE_KEY, internalCallContext);
             assertNotNull(newBundle);
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiError.java b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiError.java
index 8b3d55e..3774427 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiError.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiError.java
@@ -128,9 +128,9 @@ public class TestUserApiError extends SubscriptionTestSuiteNoDB {
             final SubscriptionBase subscription = testUtil.createSubscription(bundle, "Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
 
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            subscription.cancel(clock.getUTCNow(), callContext);
+            subscription.cancelWithDate(clock.getUTCNow(), callContext);
             try {
-                subscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), callContext);
+                subscription.changePlanWithDate("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), callContext);
             } catch (SubscriptionBaseApiException e) {
                 assertEquals(e.getCode(), ErrorCode.SUB_CHANGE_NON_ACTIVE.getCode());
                 try {
@@ -149,14 +149,14 @@ public class TestUserApiError extends SubscriptionTestSuiteNoDB {
         final SubscriptionBase subscription = testUtil.createSubscription(bundle, "Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
 
         try {
-            subscription.changePlanWithPolicy("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), BillingActionPolicy.ILLEGAL, callContext);
+            subscription.changePlanWithPolicy("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, BillingActionPolicy.ILLEGAL, callContext);
             Assert.fail();
         } catch (SubscriptionBaseError error) {
             assertTrue(true);
             assertEquals(subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext).getCurrentPlan().getBillingPeriod(), BillingPeriod.ANNUAL);
         }
 
-        assertTrue(subscription.changePlanWithPolicy("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), BillingActionPolicy.IMMEDIATE, callContext));
+        assertTrue(subscription.changePlanWithPolicy("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, BillingActionPolicy.IMMEDIATE, callContext));
         assertEquals(subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext).getCurrentPlan().getBillingPeriod(), BillingPeriod.MONTHLY);
     }
 
@@ -181,9 +181,9 @@ public class TestUserApiError extends SubscriptionTestSuiteNoDB {
 
             subscription = subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
 
-            subscription.cancel(clock.getUTCNow(), callContext);
+            subscription.cancelWithPolicy(BillingActionPolicy.END_OF_TERM, callContext);
             try {
-                subscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), callContext);
+                subscription.changePlanWithDate("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), callContext);
             } catch (SubscriptionBaseApiException e) {
                 assertEquals(e.getCode(), ErrorCode.SUB_CHANGE_FUTURE_CANCELLED.getCode());
                 try {
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiRecreate.java b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiRecreate.java
index ceea47d..c6a4242 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiRecreate.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiRecreate.java
@@ -96,7 +96,7 @@ public abstract class TestUserApiRecreate extends SubscriptionTestSuiteWithEmbed
 
         // NOW CANCEL ADN THIS SHOULD WORK
         testListener.pushExpectedEvent(NextEvent.CANCEL);
-        subscription.cancel(null, callContext);
+        subscription.cancelWithDate(null, callContext);
 
         testListener.pushExpectedEvent(NextEvent.PHASE);
         testListener.pushExpectedEvent(NextEvent.RE_CREATE);
diff --git a/util/src/test/java/com/ning/billing/mock/MockSubscription.java b/util/src/test/java/com/ning/billing/mock/MockSubscription.java
index 8d39a0b..eaa8ca8 100644
--- a/util/src/test/java/com/ning/billing/mock/MockSubscription.java
+++ b/util/src/test/java/com/ning/billing/mock/MockSubscription.java
@@ -27,7 +27,6 @@ import com.ning.billing.catalog.api.BillingActionPolicy;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
-import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PriceList;
 import com.ning.billing.catalog.api.Product;
 import com.ning.billing.catalog.api.ProductCategory;
@@ -36,7 +35,6 @@ import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
 import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
 import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.subscription.api.user.SubscriptionBaseTransition;
-import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.events.EffectiveSubscriptionInternalEvent;
 
@@ -75,15 +73,19 @@ public class MockSubscription implements SubscriptionBase {
     SubscriptionBase sub = Mockito.mock(SubscriptionBase.class);
 
     @Override
-    public boolean cancel(final DateTime requestedDate, final CallContext context) throws SubscriptionBaseApiException {
-        return sub.cancel(requestedDate, context);
+    public boolean cancel(final CallContext context) throws SubscriptionBaseApiException {
+        return sub.cancel(context);
     }
 
     @Override
-    public boolean cancelWithPolicy(DateTime requestedDate,
-                                    BillingActionPolicy policy, CallContext context)
+    public boolean cancelWithDate(final DateTime requestedDate, final CallContext context) throws SubscriptionBaseApiException {
+        return sub.cancelWithDate(requestedDate, context);
+    }
+
+    @Override
+    public boolean cancelWithPolicy(BillingActionPolicy policy, CallContext context)
             throws SubscriptionBaseApiException {
-        return sub.cancelWithPolicy(requestedDate, policy, context);
+        return sub.cancelWithPolicy(policy, context);
     }
 
     @Override
@@ -92,15 +94,20 @@ public class MockSubscription implements SubscriptionBase {
     }
 
     @Override
-    public boolean changePlan(final String productName, final BillingPeriod term, final String priceList, final DateTime requestedDate,
-                              final CallContext context) throws SubscriptionBaseApiException {
-        return sub.changePlan(productName, term, priceList, requestedDate, context);
+    public boolean changePlan(final String productName, final BillingPeriod term, final String priceList, final CallContext context) throws SubscriptionBaseApiException {
+        return sub.changePlan(productName, term, priceList, context);
+    }
+
+    @Override
+    public boolean changePlanWithDate(final String productName, final BillingPeriod term, final String priceList, final DateTime requestedDate,
+                                      final CallContext context) throws SubscriptionBaseApiException {
+        return sub.changePlanWithDate(productName, term, priceList, requestedDate, context);
     }
 
     @Override
     public boolean changePlanWithPolicy(final String productName, final BillingPeriod term, final String priceList,
-                                        final DateTime requestedDate, final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
-        return sub.changePlan(productName, term, priceList, requestedDate, context);
+                                        final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
+        return sub.changePlanWithPolicy(productName, term, priceList, policy, context);
     }
 
     @Override