killbill-uncached

Changes

Details

diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java b/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
index 9c280fe..cde8803 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
@@ -45,20 +45,23 @@ public interface Subscription extends Entity, Blockable {
         TRANSFERED
     }
 
-    public boolean cancel(DateTime requestedDate, boolean eot, CallContext context)
-            throws EntitlementUserApiException;
+    public boolean cancel(final DateTime requestedDate, final CallContext context)
+        throws EntitlementUserApiException;
+
+    public boolean cancelWithPolicy(final DateTime requestedDate, final ActionPolicy policy, final CallContext context)
+        throws EntitlementUserApiException;
 
-    public boolean uncancel(CallContext context)
+    public boolean uncancel(final CallContext context)
             throws EntitlementUserApiException;
 
-    public boolean changePlan(String productName, BillingPeriod term, String priceList, DateTime requestedDate, CallContext context)
+    public boolean changePlan(final String productName, final BillingPeriod term, final String priceList, final DateTime requestedDate, final CallContext context)
             throws EntitlementUserApiException;
 
-    public boolean changePlanWithPolicy(String productName, BillingPeriod term, String priceList, DateTime requestedDate,
-                                        ActionPolicy policy, CallContext context)
+    public boolean changePlanWithPolicy(final String productName, final BillingPeriod term, final String priceList, final DateTime requestedDate,
+            final ActionPolicy policy, final CallContext context)
             throws EntitlementUserApiException;
 
-    public boolean recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
+    public boolean recreate(final PlanPhaseSpecifier spec, final DateTime requestedDate, final CallContext context)
             throws EntitlementUserApiException;
 
     public UUID getBundleId();
diff --git a/api/src/main/java/com/ning/billing/junction/api/Blockable.java b/api/src/main/java/com/ning/billing/junction/api/Blockable.java
index 73be651..cb5b1a0 100644
--- a/api/src/main/java/com/ning/billing/junction/api/Blockable.java
+++ b/api/src/main/java/com/ning/billing/junction/api/Blockable.java
@@ -18,7 +18,6 @@ package com.ning.billing.junction.api;
 
 import java.util.UUID;
 
-import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
@@ -30,7 +29,7 @@ public interface Blockable {
         SUBSCRIPTION_BUNDLE,
         SUBSCRIPTION;
 
-        public static Type get(final Blockable o) throws BlockingApiException {
+        public static Type get(final Blockable o) {
             if (o instanceof Account) {
                 return ACCOUNT;
             } else if (o instanceof SubscriptionBundle) {
@@ -38,7 +37,7 @@ public interface Blockable {
             } else if (o instanceof Subscription) {
                 return SUBSCRIPTION;
             }
-            throw new BlockingApiException(ErrorCode.BLOCK_TYPE_NOT_SUPPORTED, o.getClass().getName());
+            throw new IllegalStateException("Unsupported type of blockable " + o);
         }
 
         public static Type get(final String type) throws BlockingApiException {
@@ -49,7 +48,7 @@ public interface Blockable {
             } else if (type.equalsIgnoreCase(SUBSCRIPTION.name())) {
                 return SUBSCRIPTION;
             }
-            throw new BlockingApiException(ErrorCode.BLOCK_TYPE_NOT_SUPPORTED, type);
+            throw new IllegalStateException("Unsupported type of blockable " + type);
         }
 
     }
diff --git a/api/src/main/java/com/ning/billing/overdue/Condition.java b/api/src/main/java/com/ning/billing/overdue/Condition.java
index 8319b48..f5fe347 100644
--- a/api/src/main/java/com/ning/billing/overdue/Condition.java
+++ b/api/src/main/java/com/ning/billing/overdue/Condition.java
@@ -16,7 +16,9 @@
 
 package com.ning.billing.overdue;
 
+
 import org.joda.time.LocalDate;
+import org.joda.time.Period;
 
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.overdue.config.api.BillingState;
@@ -31,5 +33,4 @@ public interface Condition<T extends Blockable> {
      * @return true if the condition is true, false otherwise
      */
     public boolean evaluate(BillingState<T> state, LocalDate now);
-
 }
diff --git a/api/src/main/java/com/ning/billing/overdue/config/api/OverdueStateSet.java b/api/src/main/java/com/ning/billing/overdue/config/api/OverdueStateSet.java
index e423d10..b0218d7 100644
--- a/api/src/main/java/com/ning/billing/overdue/config/api/OverdueStateSet.java
+++ b/api/src/main/java/com/ning/billing/overdue/config/api/OverdueStateSet.java
@@ -39,4 +39,6 @@ public interface OverdueStateSet<T extends Blockable> {
     public abstract OverdueState<T> calculateOverdueState(BillingState<T> billingState, LocalDate now) throws OverdueApiException;
 
     public abstract int size();
+
+    public abstract OverdueState<T> getFirstState();
 }
diff --git a/api/src/main/java/com/ning/billing/overdue/OverdueCancellationPolicicy.java b/api/src/main/java/com/ning/billing/overdue/OverdueCancellationPolicicy.java
new file mode 100644
index 0000000..dda0dd6
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/overdue/OverdueCancellationPolicicy.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.overdue;
+
+public enum OverdueCancellationPolicicy {
+    END_OF_TERM,
+    IMMEDIATE,
+    NONE
+}
diff --git a/api/src/main/java/com/ning/billing/overdue/OverdueState.java b/api/src/main/java/com/ning/billing/overdue/OverdueState.java
index 39cf93d..0c216b1 100644
--- a/api/src/main/java/com/ning/billing/overdue/OverdueState.java
+++ b/api/src/main/java/com/ning/billing/overdue/OverdueState.java
@@ -31,6 +31,8 @@ public interface OverdueState<T extends Blockable> {
 
     public boolean disableEntitlementAndChangesBlocked();
 
+    public OverdueCancellationPolicicy getSubscriptionCancellationPolicy();
+
     public boolean blockChanges();
 
     public boolean isClearState();
diff --git a/api/src/main/java/com/ning/billing/overdue/OverdueUserApi.java b/api/src/main/java/com/ning/billing/overdue/OverdueUserApi.java
index eca5b18..d06bd7e 100644
--- a/api/src/main/java/com/ning/billing/overdue/OverdueUserApi.java
+++ b/api/src/main/java/com/ning/billing/overdue/OverdueUserApi.java
@@ -18,16 +18,16 @@ package com.ning.billing.overdue;
 
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.overdue.config.api.BillingState;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 
 public interface OverdueUserApi {
 
-    public <T extends Blockable> OverdueState<T> refreshOverdueStateFor(T overdueable) throws OverdueError, OverdueApiException;
+    public <T extends Blockable> OverdueState<T> refreshOverdueStateFor(T overdueable) throws OverdueException, OverdueApiException;
 
-    public <T extends Blockable> void setOverrideBillingStateForAccount(T overdueable, BillingState<T> state) throws OverdueError;
+    public <T extends Blockable> void setOverrideBillingStateForAccount(T overdueable, BillingState<T> state) throws OverdueException;
 
-    public <T extends Blockable> OverdueState<T> getOverdueStateFor(T overdueable) throws OverdueError;
+    public <T extends Blockable> OverdueState<T> getOverdueStateFor(T overdueable) throws OverdueException;
 
-    public <T extends Blockable> BillingState<T> getBillingStateFor(T overdueable) throws OverdueError;
+    public <T extends Blockable> BillingState<T> getBillingStateFor(T overdueable) throws OverdueException;
 
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueBase.java
new file mode 100644
index 0000000..773a1eb
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueBase.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.beatrix.integration.overdue;
+
+import static com.jayway.awaitility.Awaitility.await;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.testng.Assert.assertNotNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.beatrix.integration.BeatrixModule;
+import com.ning.billing.beatrix.integration.TestIntegrationBase;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.junction.api.BlockingApi;
+import com.ning.billing.overdue.OverdueUserApi;
+import com.ning.billing.overdue.config.OverdueConfig;
+import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
+import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentMethodPlugin;
+import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.config.XMLLoader;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
+public abstract class TestOverdueBase extends TestIntegrationBase {
+
+    @Inject
+    protected ClockMock clock;
+
+    @Named("yoyo")
+    @Inject
+    protected
+    MockPaymentProviderPlugin paymentPlugin;
+
+    @Inject
+    protected BlockingApi blockingApi;
+
+    @Inject
+    protected OverdueWrapperFactory overdueWrapperFactory;
+
+    @Inject
+    protected OverdueUserApi overdueApi;
+
+    @Inject
+    protected PaymentApi paymentApi;
+
+    @Inject
+    protected InvoiceUserApi invoiceApi;
+
+    protected Account account;
+    protected SubscriptionBundle bundle;
+    protected String productName;
+    protected BillingPeriod term;
+
+
+    public abstract String getOverdueConfig();
+
+    final PaymentMethodPlugin paymentMethodPlugin = new PaymentMethodPlugin() {
+        @Override
+        public boolean isDefaultPaymentMethod() {
+            return false;
+        }
+
+        @Override
+        public String getValueString(final String key) {
+            return null;
+        }
+
+        @Override
+        public List<PaymentMethodKVInfo> getProperties() {
+            return null;
+        }
+
+        @Override
+        public String getExternalPaymentMethodId() {
+            return UUID.randomUUID().toString();
+        }
+    };
+
+    @BeforeMethod(groups = "slow")
+    public void setupOverdue() throws Exception {
+        final String configXml = getOverdueConfig();
+        final InputStream is = new ByteArrayInputStream(configXml.getBytes());
+        final OverdueConfig config = XMLLoader.getObjectFromStreamNoValidation(is, OverdueConfig.class);
+        overdueWrapperFactory.setOverdueConfig(config);
+
+        account = createAccountWithPaymentMethod(getAccountData(0));
+        assertNotNull(account);
+
+        paymentApi.addPaymentMethod(BeatrixModule.PLUGIN_NAME, account, true, paymentMethodPlugin, context);
+
+        bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", context);
+
+        productName = "Shotgun";
+        term = BillingPeriod.MONTHLY;
+
+        paymentPlugin.clear();
+    }
+
+
+    protected void checkODState(final String expected) {
+        try {
+            // This will test the overdue notification queue: when we move the clock, the overdue system
+            // should get notified to refresh its state.
+            // Calling explicitly refresh here (overdueApi.refreshOverdueStateFor(bundle)) would not fully
+            // test overdue.
+            // Since we're relying on the notification queue, we may need to wait a bit (hence await()).
+            await().atMost(10, SECONDS).until(new Callable<Boolean>() {
+                @Override
+                public Boolean call() throws Exception {
+                    return expected.equals(blockingApi.getBlockingStateFor(bundle).getStateName());
+                }
+            });
+        } catch (Exception e) {
+            Assert.assertEquals(blockingApi.getBlockingStateFor(bundle).getStateName(), expected, "Got exception: " + e.toString());
+        }
+    }
+
+
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
index 420516c..6aa69ad 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -16,25 +16,18 @@
 
 package com.ning.billing.beatrix.integration.overdue;
 
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
 import java.math.BigDecimal;
 import java.util.Collection;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.Callable;
+
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
+
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
-import com.ning.billing.account.api.Account;
 import com.ning.billing.api.TestApiListener.NextEvent;
 import com.ning.billing.beatrix.integration.BeatrixModule;
-import com.ning.billing.beatrix.integration.TestIntegrationBase;
 import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedItemCheck;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
@@ -42,149 +35,72 @@ import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.invoice.api.InvoicePayment;
-import com.ning.billing.invoice.api.InvoiceUserApi;
-import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.junction.api.BlockingApi;
 import com.ning.billing.junction.api.BlockingApiException;
-import com.ning.billing.overdue.OverdueUserApi;
-import com.ning.billing.overdue.config.OverdueConfig;
-import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
 import com.ning.billing.payment.api.Payment;
-import com.ning.billing.payment.api.PaymentApi;
-import com.ning.billing.payment.api.PaymentMethodPlugin;
-import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
-import com.ning.billing.util.clock.ClockMock;
-import com.ning.billing.util.config.XMLLoader;
 
-import com.google.inject.Inject;
-import com.google.inject.name.Named;
-
-import static com.jayway.awaitility.Awaitility.await;
-import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
 @Test(groups = "slow")
 @Guice(modules = {BeatrixModule.class})
-public class TestOverdueIntegration extends TestIntegrationBase {
-
-    @Inject
-    private ClockMock clock;
-
-    @Named("yoyo")
-    @Inject
-    private
-    MockPaymentProviderPlugin paymentPlugin;
-
-    @Inject
-    private BlockingApi blockingApi;
-
-    @Inject
-    private OverdueWrapperFactory overdueWrapperFactory;
-
-    @Inject
-    private OverdueUserApi overdueApi;
-
-    @Inject
-    private PaymentApi paymentApi;
-
-    @Inject
-    private InvoiceUserApi invoiceApi;
-
-    private Account account;
-    private SubscriptionBundle bundle;
-    private String productName;
-    private BillingPeriod term;
-
-    final PaymentMethodPlugin paymentMethodPlugin = new PaymentMethodPlugin() {
-        @Override
-        public boolean isDefaultPaymentMethod() {
-            return false;
-        }
+public class TestOverdueIntegration extends TestOverdueBase {
 
-        @Override
-        public String getValueString(final String key) {
-            return null;
-        }
-
-        @Override
-        public List<PaymentMethodKVInfo> getProperties() {
-            return null;
-        }
 
-        @Override
-        public String getExternalPaymentMethodId() {
-            return UUID.randomUUID().toString();
-        }
-    };
 
-    @BeforeMethod(groups = "slow")
-    public void setupOverdue() throws Exception {
+    @Override
+    public String getOverdueConfig() {
         final String configXml = "<overdueConfig>" +
-                                 "   <bundleOverdueStates>" +
-                                 "       <state name=\"OD3\">" +
-                                 "           <condition>" +
-                                 "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-                                 "                   <unit>DAYS</unit><number>50</number>" +
-                                 "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-                                 "           </condition>" +
-                                 "           <externalMessage>Reached OD3</externalMessage>" +
-                                 "           <blockChanges>true</blockChanges>" +
-                                 "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
-                                 "           <autoReevaluationInterval>" +
-                                 "               <unit>DAYS</unit><number>5</number>" +
-                                 "           </autoReevaluationInterval>" +
-                                 "       </state>" +
-                                 "       <state name=\"OD2\">" +
-                                 "           <condition>" +
-                                 "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-                                 "                   <unit>DAYS</unit><number>40</number>" +
-                                 "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-                                 "           </condition>" +
-                                 "           <externalMessage>Reached OD2</externalMessage>" +
-                                 "           <blockChanges>true</blockChanges>" +
-                                 "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
-                                 "           <autoReevaluationInterval>" +
-                                 "               <unit>DAYS</unit><number>5</number>" +
-                                 "           </autoReevaluationInterval>" +
-                                 "       </state>" +
-                                 "       <state name=\"OD1\">" +
-                                 "           <condition>" +
-                                 "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-                                 "                   <unit>DAYS</unit><number>30</number>" +
-                                 "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
-                                 "           </condition>" +
-                                 "           <externalMessage>Reached OD1</externalMessage>" +
-                                 "           <blockChanges>true</blockChanges>" +
-                                 "           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>" +
-                                 "           <autoReevaluationInterval>" +
-                                 "               <unit>DAYS</unit><number>5</number>" +
-                                 "           </autoReevaluationInterval>" +
-                                 "       </state>" +
-                                 "   </bundleOverdueStates>" +
-                                 "</overdueConfig>";
-        final InputStream is = new ByteArrayInputStream(configXml.getBytes());
-        final OverdueConfig config = XMLLoader.getObjectFromStreamNoValidation(is, OverdueConfig.class);
-        overdueWrapperFactory.setOverdueConfig(config);
-
-        account = createAccountWithPaymentMethod(getAccountData(0));
-        assertNotNull(account);
-
-        paymentApi.addPaymentMethod(BeatrixModule.PLUGIN_NAME, account, true, paymentMethodPlugin, context);
-
-        bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", context);
-
-        productName = "Shotgun";
-        term = BillingPeriod.MONTHLY;
-
-        paymentPlugin.clear();
+        "   <bundleOverdueStates>" +
+        "       <state name=\"OD3\">" +
+        "           <condition>" +
+        "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+        "                   <unit>DAYS</unit><number>50</number>" +
+        "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+        "           </condition>" +
+        "           <externalMessage>Reached OD3</externalMessage>" +
+        "           <blockChanges>true</blockChanges>" +
+        "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
+        "           <autoReevaluationInterval>" +
+        "               <unit>DAYS</unit><number>5</number>" +
+        "           </autoReevaluationInterval>" +
+        "       </state>" +
+        "       <state name=\"OD2\">" +
+        "           <condition>" +
+        "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+        "                   <unit>DAYS</unit><number>40</number>" +
+        "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+        "           </condition>" +
+        "           <externalMessage>Reached OD2</externalMessage>" +
+        "           <blockChanges>true</blockChanges>" +
+        "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
+        "           <autoReevaluationInterval>" +
+        "               <unit>DAYS</unit><number>5</number>" +
+        "           </autoReevaluationInterval>" +
+        "       </state>" +
+        "       <state name=\"OD1\">" +
+        "           <condition>" +
+        "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+        "                   <unit>DAYS</unit><number>30</number>" +
+        "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+        "           </condition>" +
+        "           <externalMessage>Reached OD1</externalMessage>" +
+        "           <blockChanges>true</blockChanges>" +
+        "           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>" +
+        "           <autoReevaluationInterval>" +
+        "               <unit>DAYS</unit><number>5</number>" +
+        "           </autoReevaluationInterval>" +
+        "       </state>" +
+        "   </bundleOverdueStates>" +
+        "</overdueConfig>";
+
+        return configXml;
     }
 
+
     // We set the the property killbill.payment.retry.days=8,8,8,8,8,8,8,8 so that Payment retry logic does not end with an ABORTED state
     // preventing final instant payment to succeed.
     @Test(groups = "slow")
@@ -645,22 +561,4 @@ public class TestOverdueIntegration extends TestIntegrationBase {
             changeSubscriptionAndCheckForCompletion(subscription, "Assault-Rifle", BillingPeriod.MONTHLY, NextEvent.CHANGE, NextEvent.INVOICE);
         }
     }
-
-    private void checkODState(final String expected) {
-        try {
-            // This will test the overdue notification queue: when we move the clock, the overdue system
-            // should get notified to refresh its state.
-            // Calling explicitly refresh here (overdueApi.refreshOverdueStateFor(bundle)) would not fully
-            // test overdue.
-            // Since we're relying on the notification queue, we may need to wait a bit (hence await()).
-            await().atMost(10, SECONDS).until(new Callable<Boolean>() {
-                @Override
-                public Boolean call() throws Exception {
-                    return expected.equals(blockingApi.getBlockingStateFor(bundle).getStateName());
-                }
-            });
-        } catch (Exception e) {
-            Assert.assertEquals(blockingApi.getBlockingStateFor(bundle).getStateName(), expected, "Got exception: " + e.toString());
-        }
-    }
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
new file mode 100644
index 0000000..618162b
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.beatrix.integration.overdue;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+
+import junit.framework.Assert;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.beatrix.integration.BeatrixModule;
+import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedItemCheck;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.invoice.api.InvoiceItemType;
+import com.ning.billing.junction.api.BlockingApi;
+
+@Test(groups = "slow")
+@Guice(modules = {BeatrixModule.class})
+public class TestOverdueWithSubscriptionCancellation extends TestOverdueBase {
+
+
+    @Override
+    public String getOverdueConfig() {
+        final String configXml = "<overdueConfig>" +
+        "   <bundleOverdueStates>" +
+           "       <state name=\"OD1\">" +
+        "           <condition>" +
+        "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+        "                   <unit>DAYS</unit><number>5</number>" +
+        "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+        "           </condition>" +
+        "           <externalMessage>Reached OD1</externalMessage>" +
+        "           <blockChanges>true</blockChanges>" +
+        "           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>" +
+        "           <subscriptionCancellationPolicy>IMMEDIATE</subscriptionCancellationPolicy>" +
+        "           <autoReevaluationInterval>" +
+        "               <unit>DAYS</unit><number>5</number>" +
+        "           </autoReevaluationInterval>" +
+        "       </state>" +
+        "   </bundleOverdueStates>" +
+        "</overdueConfig>";
+        return configXml;
+    }
+
+    @Test(groups = "slow")
+    public void testCheckSubscriptionCancellation() throws Exception {
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+        // Set next invoice to fail and create subscription
+        paymentPlugin.makeAllInvoicesFailWithError(true);
+        final Subscription baseSubscription = createSubscriptionAndCheckForCompletion(bundle.getId(), productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+
+        invoiceChecker.checkInvoice(account.getId(), 1, new ExpectedItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 5, 1));
+
+        // DAY 30 have to get out of trial before first payment
+        addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
+
+        invoiceChecker.checkInvoice(account.getId(), 2, new ExpectedItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkChargedThroughDate(baseSubscription.getId(), new LocalDate(2012, 6, 30));
+
+        // Should still be in clear state
+        checkODState(BlockingApi.CLEAR_STATE_NAME);
+
+        // DAY 36 -- RIGHT AFTER OD1
+        addDaysAndCheckForCompletion(6, NextEvent.CANCEL);
+
+        // Should be in OD1
+        checkODState("OD1");
+
+        Subscription cancelledBaseSubscription = entitlementUserApi.getSubscriptionFromId(baseSubscription.getId());
+        assertTrue(cancelledBaseSubscription.getState() == SubscriptionState.CANCELLED);
+    }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
index 8728ac9..0dfcc78 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
@@ -158,7 +158,7 @@ public class TestAnalytics extends TestIntegrationBase {
         // Cancel end of term - refetch the subscription to have the CTD set
         // (otherwise, cancellation would be immediate)
         subscription = entitlementUserApi.getSubscriptionFromId(subscription.getId());
-        subscription.cancel(clock.getUTCNow(), true, context);
+        subscription.cancel(clock.getUTCNow(), context);
 
         waitALittle();
 
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 b5860e8..d9efb35 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
@@ -539,7 +539,7 @@ public class TestIntegration extends TestIntegrationBase {
         assertTrue(busHandler.isCompleted(DELAY));
 
         subscription = subscriptionDataFromSubscription(entitlementUserApi.getSubscriptionFromId(subscription.getId()));
-        subscription.cancel(clock.getUTCNow(), false, context);
+        subscription.cancel(clock.getUTCNow(), context);
 
         // MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
         busHandler.pushExpectedEvent(NextEvent.CANCEL);
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
index e9c42b0..900e621 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
@@ -20,6 +20,7 @@ import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
+import java.util.concurrent.Callable;
 
 import javax.annotation.Nullable;
 
@@ -60,6 +61,7 @@ import com.ning.billing.entitlement.api.transfer.EntitlementTransferApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
@@ -69,6 +71,7 @@ import com.ning.billing.invoice.api.InvoicePaymentApi;
 import com.ning.billing.invoice.api.InvoiceService;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.invoice.model.InvoicingConfiguration;
+import com.ning.billing.junction.api.BlockingApi;
 import com.ning.billing.junction.plumbing.api.BlockingSubscription;
 import com.ning.billing.mock.MockAccountBuilder;
 import com.ning.billing.mock.api.MockBillCycleDay;
@@ -92,6 +95,8 @@ import com.google.common.base.Joiner;
 import com.google.inject.Inject;
 import com.google.inject.name.Named;
 
+import static com.jayway.awaitility.Awaitility.await;
+import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
@@ -456,7 +461,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
                     refreshedSubscription.changePlan(productName, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), context);
                     return refreshedSubscription;
                 } catch (EntitlementUserApiException e) {
-                    fail();
+                    fail(e.getMessage());
                     return null;
                 }
             }
@@ -472,7 +477,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
                 try {
                     // Need to fetch again to get latest CTD updated from the system
                     final Subscription refreshedSubscription = entitlementUserApi.getSubscriptionFromId(subscription.getId());
-                    refreshedSubscription.cancel(requestedDate, true, context);
+                    refreshedSubscription.cancel(requestedDate, context);
                     return refreshedSubscription;
                 } catch (EntitlementUserApiException e) {
                     fail();
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java
index 560516e..4949d4b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java
@@ -18,6 +18,8 @@ package com.ning.billing.entitlement.api.migration;
 
 import java.util.List;
 
+import org.joda.time.DateTime;
+
 import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
@@ -64,14 +66,13 @@ public class AccountMigrationData {
         private final List<EntitlementEvent> initialEvents;
 
         public SubscriptionMigrationData(final SubscriptionData data,
-                                         final List<EntitlementEvent> initialEvents) {
+                                         final List<EntitlementEvent> initialEvents,
+                                         final DateTime ctd) {
             super();
             // Set CTD to subscription object from MIGRATION_BILLING event
             final SubscriptionBuilder builder = new SubscriptionBuilder(data);
-            for (final EntitlementEvent cur : initialEvents) {
-                if (cur instanceof ApiEventMigrateBilling) {
-                    builder.setChargedThroughDate(cur.getEffectiveDate());
-                }
+            if (ctd != null) {
+                builder.setChargedThroughDate(ctd);
             }
             this.data = new SubscriptionData(builder);
             this.initialEvents = initialEvents;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
index 21f7e7a..1ea22b7 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
@@ -142,7 +142,7 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
                                                                                      .setBundleStartDate(migrationStartDate)
                                                                                      .setAlignStartDate(migrationStartDate),
                                                                              emptyEvents);
-        return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, ctd, events, context));
+        return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, ctd, events, context), ctd);
     }
 
     private SubscriptionMigrationData createSubscriptionMigrationDataWithBundleDate(final UUID bundleId, final ProductCategory productCategory,
@@ -158,12 +158,13 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
                                                                                      .setBundleStartDate(bundleStartDate)
                                                                                      .setAlignStartDate(migrationStartDate),
                                                                              emptyEvents);
-        return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, ctd, events, context));
+        return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, ctd, events, context), ctd);
     }
 
     private List<EntitlementEvent> toEvents(final SubscriptionData subscriptionData, final DateTime now, final DateTime ctd, final TimedMigration[] migrationEvents, final CallContext context) {
         ApiEventMigrateEntitlement creationEvent = null;
         final List<EntitlementEvent> events = new ArrayList<EntitlementEvent>(migrationEvents.length);
+        DateTime subsciptionCancelledDate = null;
         for (final TimedMigration cur : migrationEvents) {
 
             if (cur.getEventType() == EventType.PHASE) {
@@ -194,6 +195,7 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
                         events.add(new ApiEventChange(builder));
                         break;
                     case CANCEL:
+                        subsciptionCancelledDate = cur.getEventTime();
                         events.add(new ApiEventCancel(builder));
                         break;
                     default:
@@ -206,7 +208,9 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
         if (creationEvent == null || ctd == null) {
             throw new EntitlementError(String.format("Could not create migration billing event ctd = %s", ctd));
         }
-        events.add(new ApiEventMigrateBilling(creationEvent, ctd));
+        if (subsciptionCancelledDate == null || subsciptionCancelledDate.isAfter(ctd)) {
+            events.add(new ApiEventMigrateBilling(creationEvent, ctd));
+        }
         Collections.sort(events, new Comparator<EntitlementEvent>() {
             int compForApiType(final EntitlementEvent o1, final EntitlementEvent o2, final ApiEventType type) {
                 ApiEventType apiO1 = null;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionApiService.java
index 1e35021..4721c66 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionApiService.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionApiService.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -38,8 +38,11 @@ public interface SubscriptionApiService {
     public boolean recreatePlan(SubscriptionData subscription, PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
             throws EntitlementUserApiException;
 
-    public boolean cancel(SubscriptionData subscription, DateTime requestedDate, boolean eot, CallContext context)
-            throws EntitlementUserApiException;
+    public boolean cancel(SubscriptionData subscription, DateTime requestedDate, CallContext context)
+        throws EntitlementUserApiException;
+
+    public boolean cancelWithPolicy(SubscriptionData subscription, DateTime requestedDate, ActionPolicy policy, CallContext context)
+        throws EntitlementUserApiException;
 
     public boolean uncancel(SubscriptionData subscription, CallContext context)
             throws EntitlementUserApiException;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionDataRepair.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionDataRepair.java
index 66cf27c..87c71f8 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionDataRepair.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/timeline/SubscriptionDataRepair.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -99,7 +99,7 @@ public class SubscriptionDataRepair extends SubscriptionData {
                     trickleDownBPEffectForAddon(addonSubscriptions, getLastUserEventEffectiveDate(), context);
                     break;
                 case CANCEL:
-                    cancel(input.getRequestedDate(), false, context);
+                    cancel(input.getRequestedDate(), context);
                     trickleDownBPEffectForAddon(addonSubscriptions, getLastUserEventEffectiveDate(), context);
                     break;
                 case PHASE:
@@ -178,6 +178,7 @@ public class SubscriptionDataRepair extends SubscriptionData {
         }
     }
 
+    @Override
     public void rebuildTransitions(final List<EntitlementEvent> inputEvents, final Catalog catalog) {
         this.events = inputEvents;
         super.rebuildTransitions(inputEvents, catalog);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java
index 4fef890..bacb6f6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java
@@ -224,17 +224,17 @@ public class DefaultEntitlementTransferApi implements EntitlementTransferApi {
                         effectiveTransferDate.isBefore(oldSubscription.getChargedThroughDate()) ?
                             oldSubscription.getChargedThroughDate() : effectiveTransferDate;
 
-                            final EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
-                            .setSubscriptionId(cur.getId())
-                            .setActiveVersion(cur.getActiveVersion())
-                            .setProcessedDate(clock.getUTCNow())
-                            .setEffectiveDate(effectiveCancelDate)
-                            .setRequestedDate(effectiveTransferDate)
-                            .setUserToken(context.getUserToken())
-                            .setFromDisk(true));
-
-                            TransferCancelData cancelData =  new TransferCancelData(oldSubscription, cancelEvent);
-                            transferCancelDataList.add(cancelData);
+                    final EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
+                    .setSubscriptionId(cur.getId())
+                    .setActiveVersion(cur.getActiveVersion())
+                    .setProcessedDate(clock.getUTCNow())
+                    .setEffectiveDate(effectiveCancelDate)
+                    .setRequestedDate(effectiveTransferDate)
+                    .setUserToken(context.getUserToken())
+                    .setFromDisk(true));
+
+                    TransferCancelData cancelData =  new TransferCancelData(oldSubscription, cancelEvent);
+                    transferCancelDataList.add(cancelData);
                 }
 
                 // We Align with the original subscription
@@ -253,7 +253,7 @@ public class DefaultEntitlementTransferApi implements EntitlementTransferApi {
                 ImmutableList.<EntitlementEvent>of());
 
                 final List<EntitlementEvent> events = toEvents(existingEvents, subscriptionData, effectiveTransferDate, context);
-                final SubscriptionMigrationData curData = new SubscriptionMigrationData(subscriptionData, events);
+                final SubscriptionMigrationData curData = new SubscriptionMigrationData(subscriptionData, events, null);
                 subscriptionMigrationDataList.add(curData);
             }
             BundleMigrationData bundleMigrationData = new BundleMigrationData(subscriptionBundleData, subscriptionMigrationDataList);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java
index ac3e704..16814cb 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java
@@ -154,16 +154,14 @@ public class DefaultSubscriptionApiService implements SubscriptionApiService {
     }
 
     @Override
-    public boolean cancel(final SubscriptionData subscription, final DateTime requestedDateWithMs, final boolean eot, final CallContext context) throws EntitlementUserApiException {
+    public boolean cancel(final SubscriptionData subscription, final DateTime requestedDateWithMs, final CallContext context) throws EntitlementUserApiException {
         try {
             final SubscriptionState currentState = subscription.getState();
             if (currentState != SubscriptionState.ACTIVE) {
                 throw new EntitlementUserApiException(ErrorCode.ENT_CANCEL_BAD_STATE, subscription.getId(), currentState);
             }
-
             final DateTime now = clock.getUTCNow();
             final DateTime requestedDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
-            validateRequestedDate(subscription, now, requestedDate);
 
             final Plan currentPlan = subscription.getCurrentPlan();
             final PlanPhaseSpecifier planPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
@@ -173,26 +171,45 @@ public class DefaultSubscriptionApiService implements SubscriptionApiService {
                                                                         subscription.getCurrentPhase().getPhaseType());
 
             final ActionPolicy policy = catalogService.getFullCatalog().planCancelPolicy(planPhase, requestedDate);
-            final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, requestedDate);
 
-            final EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
-            .setSubscriptionId(subscription.getId())
-            .setActiveVersion(subscription.getActiveVersion())
-            .setProcessedDate(now)
-            .setEffectiveDate(effectiveDate)
-            .setRequestedDate(requestedDate)
-            .setUserToken(context.getUserToken())
-            .setFromDisk(true));
-
-            dao.cancelSubscription(subscription, cancelEvent, context, 0);
-            subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
-            return (policy == ActionPolicy.IMMEDIATE);
+            return doCancelPlan(subscription, requestedDateWithMs, now, policy, context);
         } catch (CatalogApiException e) {
             throw new EntitlementUserApiException(e);
         }
     }
 
     @Override
+    public boolean cancelWithPolicy(final SubscriptionData subscription, final DateTime requestedDateWithMs, final ActionPolicy policy, final CallContext context) throws EntitlementUserApiException {
+        final SubscriptionState currentState = subscription.getState();
+        if (currentState != SubscriptionState.ACTIVE) {
+            throw new EntitlementUserApiException(ErrorCode.ENT_CANCEL_BAD_STATE, subscription.getId(), currentState);
+        }
+        final DateTime now = clock.getUTCNow();
+        return doCancelPlan(subscription, requestedDateWithMs, now, policy, context);
+    }
+
+    private boolean doCancelPlan(final SubscriptionData subscription, final DateTime requestedDateWithMs, final DateTime now, final ActionPolicy policy, final CallContext context) throws EntitlementUserApiException {
+
+        final DateTime requestedDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
+        validateRequestedDate(subscription, now, requestedDate);
+        final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, requestedDate);
+
+        final EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
+        .setSubscriptionId(subscription.getId())
+        .setActiveVersion(subscription.getActiveVersion())
+        .setProcessedDate(now)
+        .setEffectiveDate(effectiveDate)
+        .setRequestedDate(requestedDate)
+        .setUserToken(context.getUserToken())
+        .setFromDisk(true));
+
+        dao.cancelSubscription(subscription, cancelEvent, context, 0);
+        subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
+        return (policy == ActionPolicy.IMMEDIATE);
+    }
+
+
+    @Override
     public boolean uncancel(final SubscriptionData subscription, final CallContext context) throws EntitlementUserApiException {
         if (!subscription.isSubscriptionFutureCancelled()) {
             throw new EntitlementUserApiException(ErrorCode.ENT_UNCANCEL_BAD_STATE, subscription.getId().toString());
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
index db4f78f..4e8c923 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
@@ -181,9 +181,13 @@ public class SubscriptionData extends EntityBase implements Subscription {
 
 
     @Override
-    public boolean cancel(final DateTime requestedDate, final boolean eot,
-            final CallContext context) throws EntitlementUserApiException {
-        return apiService.cancel(this, requestedDate, eot, context);
+    public boolean cancel(final DateTime requestedDate, final CallContext context) throws EntitlementUserApiException {
+        return apiService.cancel(this, requestedDate, context);
+    }
+
+    @Override
+    public boolean cancelWithPolicy(final DateTime requestedDate, final ActionPolicy policy, final CallContext context) throws EntitlementUserApiException {
+        return apiService.cancelWithPolicy(this, requestedDate, policy, context);
     }
 
     @Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java
index d2cd173..eb31743 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java
@@ -405,8 +405,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
             @Override
             public Void inTransaction(final EntitlementEventSqlDao transactional, final TransactionStatus status) throws Exception {
                 final UUID subscriptionId = subscription.getId();
-                cancelNextChangeEventFromTransaction(subscriptionId, transactional, context);
-                cancelNextPhaseEventFromTransaction(subscriptionId, transactional, context);
+                cancelFutureEventsFromTransaction(subscriptionId, transactional, context);
 
                 final List<EntityAudit> eventAudits = new ArrayList<EntityAudit>();
                 for (final EntitlementEvent cur : changeEvents) {
@@ -432,9 +431,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
     private void cancelSubscriptionFromTransaction(final SubscriptionData subscription, final EntitlementEvent cancelEvent, final EntitlementEventSqlDao transactional, final CallContext context, final int seqId) {
         final UUID subscriptionId = subscription.getId();
-        cancelNextCancelEventFromTransaction(subscriptionId, transactional, context);
-        cancelNextChangeEventFromTransaction(subscriptionId, transactional, context);
-        cancelNextPhaseEventFromTransaction(subscriptionId, transactional, context);
+        cancelFutureEventsFromTransaction(subscriptionId, transactional, context);
         transactional.insertEvent(cancelEvent, context);
         final String cancelEventId = cancelEvent.getId().toString();
 
@@ -456,12 +453,13 @@ public class AuditedEntitlementDao implements EntitlementDao {
         cancelFutureEventFromTransaction(subscriptionId, dao, EventType.PHASE, null, context);
     }
 
-    private void cancelNextChangeEventFromTransaction(final UUID subscriptionId, final EntitlementEventSqlDao dao, final CallContext context) {
-        cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CHANGE, context);
-    }
 
-    private void cancelNextCancelEventFromTransaction(final UUID subscriptionId, final EntitlementEventSqlDao dao, final CallContext context) {
-        cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CANCEL, context);
+    private void cancelFutureEventsFromTransaction(final UUID subscriptionId, final EntitlementEventSqlDao dao, final CallContext context) {
+        final Date now = clock.getUTCNow().toDate();
+        final List<EntitlementEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
+        for (final EntitlementEvent cur : events) {
+            unactivateEventFromTransaction(cur, dao, context);
+        }
     }
 
     private void cancelFutureEventFromTransaction(final UUID subscriptionId, final EntitlementEventSqlDao dao, final EventType type,
@@ -479,9 +477,12 @@ public class AuditedEntitlementDao implements EntitlementDao {
                 futureEvent = cur;
             }
         }
+        unactivateEventFromTransaction(futureEvent, dao, context);
+    }
 
-        if (futureEvent != null) {
-            final String eventId = futureEvent.getId().toString();
+    private void unactivateEventFromTransaction(final EntitlementEvent event, final EntitlementEventSqlDao dao, final CallContext context) {
+        if (event != null) {
+            final String eventId = event.getId().toString();
             dao.unactiveEvent(eventId, context);
             final Long recordId = dao.getRecordId(eventId);
             final EntityAudit audit = new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.UPDATE);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
index a15cd77..0e6bfe2 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
@@ -50,7 +50,7 @@ public abstract class TestMigration extends TestApiBase {
         try {
             final DateTime startDate = clock.getUTCNow().minusMonths(2);
             final DateTime beforeMigration = clock.getUTCNow();
-            final EntitlementAccountMigration toBeMigrated = createAccountWithRegularBasePlan(startDate);
+            final EntitlementAccountMigration toBeMigrated = createAccountForMigrationWithRegularBasePlan(startDate);
             final DateTime afterMigration = clock.getUTCNow();
 
             testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
@@ -83,7 +83,7 @@ public abstract class TestMigration extends TestApiBase {
             final DateTime beforeMigration = clock.getUTCNow();
             final DateTime initalBPStart = clock.getUTCNow().minusMonths(3);
             final DateTime initalAddonStart = clock.getUTCNow().minusMonths(1).plusDays(7);
-            final EntitlementAccountMigration toBeMigrated = createAccountWithRegularBasePlanAndAddons(initalBPStart, initalAddonStart);
+            final EntitlementAccountMigration toBeMigrated = createAccountForMigrationWithRegularBasePlanAndAddons(initalBPStart, initalAddonStart);
             final DateTime afterMigration = clock.getUTCNow();
 
             testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
@@ -111,7 +111,7 @@ public abstract class TestMigration extends TestApiBase {
             final Subscription aoSubscription = (subscriptions.get(0).getCurrentPlan().getProduct().getCategory() == ProductCategory.ADD_ON) ?
                     subscriptions.get(0) : subscriptions.get(1);
             // initalAddonStart.plusMonths(1).minusMonths(1) may be different from initalAddonStart, depending on exact date
-            // e.g : March 31 + 1 month => April 30 and April 30 - 1 month = March 30 which is != March 31 !!!! 
+            // e.g : March 31 + 1 month => April 30 and April 30 - 1 month = March 30 which is != March 31 !!!!
             assertEquals(aoSubscription.getStartDate(), initalAddonStart.plusMonths(1).minusMonths(1));
             assertEquals(aoSubscription.getEndDate(), null);
             assertEquals(aoSubscription.getCurrentPriceList().getName(), PriceListSet.DEFAULT_PRICELIST_NAME);
@@ -131,7 +131,7 @@ public abstract class TestMigration extends TestApiBase {
         try {
             final DateTime startDate = clock.getUTCNow().minusMonths(1);
             final DateTime beforeMigration = clock.getUTCNow();
-            final EntitlementAccountMigration toBeMigrated = createAccountWithRegularBasePlanFutreCancelled(startDate);
+            final EntitlementAccountMigration toBeMigrated = createAccountForMigrationWithRegularBasePlanFutreCancelled(startDate);
             final DateTime afterMigration = clock.getUTCNow();
 
             testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
@@ -153,7 +153,8 @@ public abstract class TestMigration extends TestApiBase {
             assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-annual");
             assertEquals(subscription.getChargedThroughDate(), startDate.plusYears(1));
 
-            testListener.pushExpectedEvent(NextEvent.MIGRATE_BILLING);
+            // The MIGRATE_BILLING will not be there because the subscription is cancelled at the same date so no BILLING should occur
+            //testListener.pushExpectedEvent(NextEvent.MIGRATE_BILLING);
             testListener.pushExpectedEvent(NextEvent.CANCEL);
 
             final Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusYears(1));
@@ -177,7 +178,7 @@ public abstract class TestMigration extends TestApiBase {
     public void testSingleBasePlanWithPendingPhase() {
         try {
             final DateTime trialDate = clock.getUTCNow().minusDays(10);
-            final EntitlementAccountMigration toBeMigrated = createAccountFuturePendingPhase(trialDate);
+            final EntitlementAccountMigration toBeMigrated = createAccountForMigrationFuturePendingPhase(trialDate);
 
             testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
             migrationApi.migrate(toBeMigrated, context);
@@ -223,7 +224,7 @@ public abstract class TestMigration extends TestApiBase {
     public void testSingleBasePlanWithPendingChange() {
         try {
             final DateTime beforeMigration = clock.getUTCNow();
-            final EntitlementAccountMigration toBeMigrated = createAccountFuturePendingChange();
+            final EntitlementAccountMigration toBeMigrated = createAccountForMigrationFuturePendingChange();
             final DateTime afterMigration = clock.getUTCNow();
 
             testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
@@ -264,179 +265,4 @@ public abstract class TestMigration extends TestApiBase {
             Assert.fail("", e);
         }
     }
-
-    private EntitlementAccountMigration createAccountTest(final List<List<EntitlementSubscriptionMigrationCaseWithCTD>> cases) {
-        return new EntitlementAccountMigration() {
-            private final UUID accountId = UUID.randomUUID();
-
-            @Override
-            public EntitlementBundleMigration[] getBundles() {
-                final List<EntitlementBundleMigration> bundles = new ArrayList<EntitlementBundleMigration>();
-                final EntitlementBundleMigration bundle0 = new EntitlementBundleMigration() {
-                    @Override
-                    public EntitlementSubscriptionMigration[] getSubscriptions() {
-                        final EntitlementSubscriptionMigration[] result = new EntitlementSubscriptionMigration[cases.size()];
-
-                        for (int i = 0; i < cases.size(); i++) {
-                            final List<EntitlementSubscriptionMigrationCaseWithCTD> curCases = cases.get(i);
-                            final EntitlementSubscriptionMigration subscription = new EntitlementSubscriptionMigration() {
-                                @Override
-                                public EntitlementSubscriptionMigrationCaseWithCTD[] getSubscriptionCases() {
-                                    return curCases.toArray(new EntitlementSubscriptionMigrationCaseWithCTD[curCases.size()]);
-                                }
-
-                                @Override
-                                public ProductCategory getCategory() {
-                                    return curCases.get(0).getPlanPhaseSpecifier().getProductCategory();
-                                }
-
-                                @Override
-                                public DateTime getChargedThroughDate() {
-                                    for (final EntitlementSubscriptionMigrationCaseWithCTD cur : curCases) {
-                                        if (cur.getChargedThroughDate() != null) {
-                                            return cur.getChargedThroughDate();
-                                        }
-                                    }
-                                    return null;
-                                }
-                            };
-                            result[i] = subscription;
-                        }
-                        return result;
-                    }
-
-                    @Override
-                    public String getBundleKey() {
-                        return "12345";
-                    }
-                };
-                bundles.add(bundle0);
-                return bundles.toArray(new EntitlementBundleMigration[bundles.size()]);
-            }
-
-            @Override
-            public UUID getAccountKey() {
-                return accountId;
-            }
-        };
-    }
-
-    private EntitlementAccountMigration createAccountWithRegularBasePlanAndAddons(final DateTime initialBPstart, final DateTime initalAddonStart) {
-
-        final List<EntitlementSubscriptionMigrationCaseWithCTD> cases = new LinkedList<EntitlementSubscriptionMigrationCaseWithCTD>();
-        cases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
-                initialBPstart,
-                null,
-                initialBPstart.plusYears(1)));
-
-        final List<EntitlementSubscriptionMigrationCaseWithCTD> firstAddOnCases = new LinkedList<EntitlementSubscriptionMigrationCaseWithCTD>();
-        firstAddOnCases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.DISCOUNT),
-                initalAddonStart,
-                initalAddonStart.plusMonths(1),
-                initalAddonStart.plusMonths(1)));
-        firstAddOnCases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
-                initalAddonStart.plusMonths(1),
-                null,
-                null));
-
-        final List<List<EntitlementSubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<EntitlementSubscriptionMigrationCaseWithCTD>>();
-        input.add(cases);
-        input.add(firstAddOnCases);
-        return createAccountTest(input);
-    }
-
-    private EntitlementAccountMigration createAccountWithRegularBasePlan(final DateTime startDate) {
-        final List<EntitlementSubscriptionMigrationCaseWithCTD> cases = new LinkedList<EntitlementSubscriptionMigrationCaseWithCTD>();
-        cases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
-                startDate,
-                null,
-                startDate.plusYears(1)));
-        final List<List<EntitlementSubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<EntitlementSubscriptionMigrationCaseWithCTD>>();
-        input.add(cases);
-        return createAccountTest(input);
-    }
-
-    private EntitlementAccountMigration createAccountWithRegularBasePlanFutreCancelled(final DateTime startDate) {
-        final List<EntitlementSubscriptionMigrationCaseWithCTD> cases = new LinkedList<EntitlementSubscriptionMigrationCaseWithCTD>();
-        cases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
-                startDate,
-                startDate.plusYears(1),
-                startDate.plusYears(1)));
-        final List<List<EntitlementSubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<EntitlementSubscriptionMigrationCaseWithCTD>>();
-        input.add(cases);
-        return createAccountTest(input);
-    }
-
-    private EntitlementAccountMigration createAccountFuturePendingPhase(final DateTime trialDate) {
-        final List<EntitlementSubscriptionMigrationCaseWithCTD> cases = new LinkedList<EntitlementSubscriptionMigrationCaseWithCTD>();
-        cases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL),
-                trialDate,
-                trialDate.plusDays(30),
-                trialDate.plusDays(30)));
-        cases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
-                trialDate.plusDays(30),
-                null,
-                null));
-        final List<List<EntitlementSubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<EntitlementSubscriptionMigrationCaseWithCTD>>();
-        input.add(cases);
-        return createAccountTest(input);
-    }
-
-    private EntitlementAccountMigration createAccountFuturePendingChange() {
-        final List<EntitlementSubscriptionMigrationCaseWithCTD> cases = new LinkedList<EntitlementSubscriptionMigrationCaseWithCTD>();
-        final DateTime effectiveDate = clock.getUTCNow().minusDays(10);
-        cases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
-                effectiveDate,
-                effectiveDate.plusMonths(1),
-                effectiveDate.plusMonths(1)));
-        cases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
-                effectiveDate.plusMonths(1).plusDays(1),
-                null,
-                null));
-        final List<List<EntitlementSubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<EntitlementSubscriptionMigrationCaseWithCTD>>();
-        input.add(cases);
-        return createAccountTest(input);
-    }
-
-    public static class EntitlementSubscriptionMigrationCaseWithCTD implements EntitlementSubscriptionMigrationCase {
-        private final PlanPhaseSpecifier pps;
-        private final DateTime effDt;
-        private final DateTime cancelDt;
-        private final DateTime ctd;
-
-        public EntitlementSubscriptionMigrationCaseWithCTD(final PlanPhaseSpecifier pps, final DateTime effDt, final DateTime cancelDt, final DateTime ctd) {
-            this.pps = pps;
-            this.cancelDt = cancelDt;
-            this.effDt = effDt;
-            this.ctd = ctd;
-        }
-
-        @Override
-        public PlanPhaseSpecifier getPlanPhaseSpecifier() {
-            return pps;
-        }
-
-        @Override
-        public DateTime getEffectiveDate() {
-            return effDt;
-        }
-
-        @Override
-        public DateTime getCancelledDate() {
-            return cancelDt;
-        }
-
-        public DateTime getChargedThroughDate() {
-            return ctd;
-        }
-    }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
index 88b24b1..eb52dec 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
@@ -19,6 +19,8 @@ package com.ning.billing.entitlement.api;
 import javax.annotation.Nullable;
 import java.io.IOException;
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
 
@@ -49,6 +51,7 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.Duration;
 import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.catalog.api.TimeUnit;
 import com.ning.billing.config.EntitlementConfig;
@@ -56,9 +59,12 @@ import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
 import com.ning.billing.entitlement.api.billing.ChargeThruApi;
 import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementAccountMigration;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementBundleMigration;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementSubscriptionMigration;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementSubscriptionMigrationCase;
 import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
 import com.ning.billing.entitlement.api.transfer.EntitlementTransferApi;
-import com.ning.billing.entitlement.api.transfer.EntitlementTransferApiException;
 import com.ning.billing.entitlement.api.user.EffectiveSubscriptionEvent;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
@@ -400,4 +406,184 @@ public abstract class TestApiBase extends EntitlementTestSuiteWithEmbeddedDB imp
             log.debug("Transition " + cur);
         }
     }
+
+
+    /**************************************************************
+        Utilities for migration tests
+    ***************************************************************/
+
+    protected EntitlementAccountMigration createAccountForMigrationTest(final List<List<EntitlementSubscriptionMigrationCaseWithCTD>> cases) {
+        return new EntitlementAccountMigration() {
+            private final UUID accountId = UUID.randomUUID();
+
+            @Override
+            public EntitlementBundleMigration[] getBundles() {
+                final List<EntitlementBundleMigration> bundles = new ArrayList<EntitlementBundleMigration>();
+                final EntitlementBundleMigration bundle0 = new EntitlementBundleMigration() {
+                    @Override
+                    public EntitlementSubscriptionMigration[] getSubscriptions() {
+                        final EntitlementSubscriptionMigration[] result = new EntitlementSubscriptionMigration[cases.size()];
+
+                        for (int i = 0; i < cases.size(); i++) {
+                            final List<EntitlementSubscriptionMigrationCaseWithCTD> curCases = cases.get(i);
+                            final EntitlementSubscriptionMigration subscription = new EntitlementSubscriptionMigration() {
+                                @Override
+                                public EntitlementSubscriptionMigrationCaseWithCTD[] getSubscriptionCases() {
+                                    return curCases.toArray(new EntitlementSubscriptionMigrationCaseWithCTD[curCases.size()]);
+                                }
+
+                                @Override
+                                public ProductCategory getCategory() {
+                                    return curCases.get(0).getPlanPhaseSpecifier().getProductCategory();
+                                }
+
+                                @Override
+                                public DateTime getChargedThroughDate() {
+                                    for (final EntitlementSubscriptionMigrationCaseWithCTD cur : curCases) {
+                                        if (cur.getChargedThroughDate() != null) {
+                                            return cur.getChargedThroughDate();
+                                        }
+                                    }
+                                    return null;
+                                }
+                            };
+                            result[i] = subscription;
+                        }
+                        return result;
+                    }
+
+                    @Override
+                    public String getBundleKey() {
+                        return "12345";
+                    }
+                };
+                bundles.add(bundle0);
+                return bundles.toArray(new EntitlementBundleMigration[bundles.size()]);
+            }
+
+            @Override
+            public UUID getAccountKey() {
+                return accountId;
+            }
+        };
+    }
+
+    protected EntitlementAccountMigration createAccountForMigrationWithRegularBasePlanAndAddons(final DateTime initialBPstart, final DateTime initalAddonStart) {
+
+        final List<EntitlementSubscriptionMigrationCaseWithCTD> cases = new LinkedList<EntitlementSubscriptionMigrationCaseWithCTD>();
+        cases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
+                new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
+                initialBPstart,
+                null,
+                initialBPstart.plusYears(1)));
+
+        final List<EntitlementSubscriptionMigrationCaseWithCTD> firstAddOnCases = new LinkedList<EntitlementSubscriptionMigrationCaseWithCTD>();
+        firstAddOnCases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
+                new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.DISCOUNT),
+                initalAddonStart,
+                initalAddonStart.plusMonths(1),
+                initalAddonStart.plusMonths(1)));
+        firstAddOnCases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
+                new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
+                initalAddonStart.plusMonths(1),
+                null,
+                null));
+
+        final List<List<EntitlementSubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<EntitlementSubscriptionMigrationCaseWithCTD>>();
+        input.add(cases);
+        input.add(firstAddOnCases);
+        return createAccountForMigrationTest(input);
+    }
+
+    protected EntitlementAccountMigration createAccountForMigrationWithRegularBasePlan(final DateTime startDate) {
+        final List<EntitlementSubscriptionMigrationCaseWithCTD> cases = new LinkedList<EntitlementSubscriptionMigrationCaseWithCTD>();
+        cases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
+                new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
+                startDate,
+                null,
+                startDate.plusYears(1)));
+        final List<List<EntitlementSubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<EntitlementSubscriptionMigrationCaseWithCTD>>();
+        input.add(cases);
+        return createAccountForMigrationTest(input);
+    }
+
+    protected EntitlementAccountMigration createAccountForMigrationWithRegularBasePlanFutreCancelled(final DateTime startDate) {
+        final List<EntitlementSubscriptionMigrationCaseWithCTD> cases = new LinkedList<EntitlementSubscriptionMigrationCaseWithCTD>();
+        cases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
+                new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
+                startDate,
+                startDate.plusYears(1),
+                startDate.plusYears(1)));
+        final List<List<EntitlementSubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<EntitlementSubscriptionMigrationCaseWithCTD>>();
+        input.add(cases);
+        return createAccountForMigrationTest(input);
+    }
+
+    protected EntitlementAccountMigration createAccountForMigrationFuturePendingPhase(final DateTime trialDate) {
+        final List<EntitlementSubscriptionMigrationCaseWithCTD> cases = new LinkedList<EntitlementSubscriptionMigrationCaseWithCTD>();
+        cases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
+                new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL),
+                trialDate,
+                trialDate.plusDays(30),
+                trialDate.plusDays(30)));
+        cases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
+                new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
+                trialDate.plusDays(30),
+                null,
+                null));
+        final List<List<EntitlementSubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<EntitlementSubscriptionMigrationCaseWithCTD>>();
+        input.add(cases);
+        return createAccountForMigrationTest(input);
+    }
+
+    protected EntitlementAccountMigration createAccountForMigrationFuturePendingChange() {
+        final List<EntitlementSubscriptionMigrationCaseWithCTD> cases = new LinkedList<EntitlementSubscriptionMigrationCaseWithCTD>();
+        final DateTime effectiveDate = clock.getUTCNow().minusDays(10);
+        cases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
+                new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
+                effectiveDate,
+                effectiveDate.plusMonths(1),
+                effectiveDate.plusMonths(1)));
+        cases.add(new EntitlementSubscriptionMigrationCaseWithCTD(
+                new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
+                effectiveDate.plusMonths(1).plusDays(1),
+                null,
+                null));
+        final List<List<EntitlementSubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<EntitlementSubscriptionMigrationCaseWithCTD>>();
+        input.add(cases);
+        return createAccountForMigrationTest(input);
+    }
+
+    public static class EntitlementSubscriptionMigrationCaseWithCTD implements EntitlementSubscriptionMigrationCase {
+        private final PlanPhaseSpecifier pps;
+        private final DateTime effDt;
+        private final DateTime cancelDt;
+        private final DateTime ctd;
+
+        public EntitlementSubscriptionMigrationCaseWithCTD(final PlanPhaseSpecifier pps, final DateTime effDt, final DateTime cancelDt, final DateTime ctd) {
+            this.pps = pps;
+            this.cancelDt = cancelDt;
+            this.effDt = effDt;
+            this.ctd = ctd;
+        }
+
+        @Override
+        public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+            return pps;
+        }
+
+        @Override
+        public DateTime getEffectiveDate() {
+            return effDt;
+        }
+
+        @Override
+        public DateTime getCancelledDate() {
+            return cancelDt;
+        }
+
+        public DateTime getChargedThroughDate() {
+            return ctd;
+        }
+    }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java
index 6074c5e..146db74 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestTransfer.java
@@ -38,7 +38,10 @@ import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.Product;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
 import com.ning.billing.entitlement.api.TestApiBase;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApiException;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementAccountMigration;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
@@ -55,6 +58,67 @@ public class TestTransfer extends TestApiBase {
         return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
     }
 
+
+
+    @Test(groups = "slow")
+    public void testTransferMigratedSubscriptionWithCTDInFuture() throws Exception {
+
+        final UUID newAccountId = UUID.randomUUID();
+
+        try {
+            final DateTime startDate = clock.getUTCNow().minusMonths(2);
+            final DateTime beforeMigration = clock.getUTCNow();
+            final EntitlementAccountMigration toBeMigrated = createAccountForMigrationWithRegularBasePlan(startDate);
+            final DateTime afterMigration = clock.getUTCNow();
+
+            testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+            migrationApi.migrate(toBeMigrated, context);
+            assertTrue(testListener.isCompleted(5000));
+
+            final List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
+            assertEquals(bundles.size(), 1);
+            final SubscriptionBundle bundle = bundles.get(0);
+
+            final List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(bundle.getId());
+            assertEquals(subscriptions.size(), 1);
+            final Subscription subscription = subscriptions.get(0);
+            assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
+            assertEquals(subscription.getEndDate(), null);
+            assertEquals(subscription.getCurrentPriceList().getName(), PriceListSet.DEFAULT_PRICELIST_NAME);
+            assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+            assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
+            assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-annual");
+            assertEquals(subscription.getChargedThroughDate(), startDate.plusYears(1));
+            // WE should see MIGRATE_ENTITLEMENT and then MIGRATE_BILLING in the future
+            assertEquals(subscription.getBillingTransitions().size(), 1);
+            assertEquals(subscription.getBillingTransitions().get(0).getTransitionType(), SubscriptionTransitionType.MIGRATE_BILLING);
+            assertTrue(subscription.getBillingTransitions().get(0).getEffectiveTransitionTime().compareTo(clock.getUTCNow()) > 0);
+            assertListenerStatus();
+
+
+            // MOVE A LITTLE, STILL IN TRIAL
+            clock.addDays(20);
+
+            final DateTime transferRequestedDate = clock.getUTCNow();
+
+            testListener.pushExpectedEvent(NextEvent.TRANSFER);
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
+            transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getKey(), transferRequestedDate, false, true, context);
+            assertTrue(testListener.isCompleted(3000));
+
+            final Subscription oldBaseSubscription = entitlementApi.getBaseSubscription(bundle.getId());
+            assertTrue(oldBaseSubscription.getState() == SubscriptionState.CANCELLED);
+            // The MIGRATE_BILLING event should have been invalidated
+            assertEquals(oldBaseSubscription.getBillingTransitions().size(), 1);
+            assertEquals(oldBaseSubscription.getBillingTransitions().get(0).getTransitionType(), SubscriptionTransitionType.CANCEL);
+
+        } catch (EntitlementMigrationApiException e) {
+            Assert.fail("", e);
+        }
+
+
+    }
+
     @Test(groups = "slow")
     public void testTransferBPInTrialWithNoCTD() throws Exception {
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
index 2ec89a3..3dd0927 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
@@ -71,7 +71,7 @@ public class TestUserApiAddOn extends TestApiBase {
             assertEquals(aoSubscription.getState(), SubscriptionState.ACTIVE);
 
             final DateTime now = clock.getUTCNow();
-            aoSubscription.cancel(now, false, context);
+            aoSubscription.cancel(now, context);
 
             testListener.reset();
             testListener.pushExpectedEvent(NextEvent.CANCEL);
@@ -119,7 +119,7 @@ public class TestUserApiAddOn extends TestApiBase {
             baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
 
             // FUTURE CANCELLATION
-            baseSubscription.cancel(now, false, context);
+            baseSubscription.cancel(now, context);
 
             // REFETCH AO SUBSCRIPTION AND CHECK THIS IS ACTIVE
             aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
index 8e70eee..6e3becc 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
@@ -64,7 +64,7 @@ public abstract class TestUserApiCancel extends TestApiBase {
 
 
             // CANCEL in trial period to get IMM policy
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
             currentPhase = subscription.getCurrentPhase();
             testListener.isCompleted(3000);
 
@@ -121,7 +121,7 @@ public abstract class TestUserApiCancel extends TestApiBase {
             // CANCEL
             testListener.setNonExpectedMode();
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
             assertFalse(testListener.isCompleted(3000));
             testListener.reset();
 
@@ -186,7 +186,7 @@ public abstract class TestUserApiCancel extends TestApiBase {
             testListener.pushExpectedEvent(NextEvent.CANCEL);
 
             // CANCEL
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
             assertTrue(testListener.isCompleted(5000));
 
             final PlanPhase currentPhase = subscription.getCurrentPhase();
@@ -232,7 +232,7 @@ public abstract class TestUserApiCancel extends TestApiBase {
             subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
 
             // CANCEL EOT
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
 
             subscription.uncancel(context);
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
index dffd3de..29e8a87 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
@@ -159,7 +159,7 @@ public class TestUserApiDemos extends TestApiBase {
 
             /* STEP 8. CANCEL IMM (NO CTD) */
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
 
             displayState(subscription.getId(), "STEP 8.  CANCELLATION");
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
index 3beb9a9..f26bc35 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
@@ -146,7 +146,7 @@ public class TestUserApiError extends TestApiBase {
             final Subscription subscription = createSubscription("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
 
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
             try {
                 subscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), context);
             } catch (EntitlementUserApiException e) {
@@ -199,7 +199,7 @@ public class TestUserApiError extends TestApiBase {
 
             subscription = entitlementApi.getSubscriptionFromId(subscription.getId());
 
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
             try {
                 subscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), context);
             } catch (EntitlementUserApiException e) {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java
index d35c9f3..026f262 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java
@@ -92,7 +92,7 @@ public abstract class TestUserApiRecreate extends TestApiBase {
 
         // NOW CANCEL ADN THIS SHOULD WORK
         testListener.pushExpectedEvent(NextEvent.CANCEL);
-        subscription.cancel(null, false, context);
+        subscription.cancel(null, context);
 
         testListener.pushExpectedEvent(NextEvent.PHASE);
         testListener.pushExpectedEvent(NextEvent.RE_CREATE);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
index e53c706..8859d69 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
@@ -71,7 +71,7 @@ public class TestUserApiScenarios extends TestApiBase {
             // CANCEL EOT
             testListener.setNonExpectedMode();
             testListener.pushExpectedEvent(NextEvent.CANCEL);
-            subscription.cancel(clock.getUTCNow(), false, context);
+            subscription.cancel(clock.getUTCNow(), context);
             assertFalse(testListener.isCompleted(5000));
             testListener.reset();
 
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
index c1c3154..140fc67 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -212,7 +212,7 @@ public class TestInvoiceDispatcher extends InvoicingTestBase {
         final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, billingApi, invoiceDao,
                                                                    invoiceNotifier, locker, busService.getBus(), clock);
 
-        final Invoice invoice = dispatcher.processAccount(account.getId(), new DateTime("2012-07-31T00:00:00.000Z"), false, context);
+        final Invoice invoice = dispatcher.processAccount(account.getId(), new DateTime("2012-07-30T00:00:00.000Z"), false, context);
         Assert.assertNotNull(invoice);
 
         final List<InvoiceItem> invoiceItems = invoice.getInvoiceItems();
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueErrorMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueErrorMapper.java
index 98ac732..12c3a5f 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueErrorMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueErrorMapper.java
@@ -23,11 +23,11 @@ import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.ext.ExceptionMapper;
 import javax.ws.rs.ext.Provider;
 
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 
 @Singleton
 @Provider
-public class OverdueErrorMapper extends ExceptionMapperBase implements ExceptionMapper<OverdueError> {
+public class OverdueErrorMapper extends ExceptionMapperBase implements ExceptionMapper<OverdueException> {
 
     private final UriInfo uriInfo;
 
@@ -36,7 +36,7 @@ public class OverdueErrorMapper extends ExceptionMapperBase implements Exception
     }
 
     @Override
-    public Response toResponse(final OverdueError exception) {
+    public Response toResponse(final OverdueException exception) {
         return buildBadRequestResponse(exception, uriInfo);
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/OverdueResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/OverdueResource.java
index b4c6f64..3fcc1fc 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/OverdueResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/OverdueResource.java
@@ -36,7 +36,7 @@ import com.ning.billing.jaxrs.json.OverdueStateJson;
 import com.ning.billing.overdue.OverdueApiException;
 import com.ning.billing.overdue.OverdueState;
 import com.ning.billing.overdue.OverdueUserApi;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -63,7 +63,7 @@ public class OverdueResource implements JaxrsResource {
     @GET
     @Path("/" + ACCOUNTS + "/{accountId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
-    public Response getOverdueAccount(@PathParam("accountId") final String accountId) throws AccountApiException, OverdueError, OverdueApiException {
+    public Response getOverdueAccount(@PathParam("accountId") final String accountId) throws AccountApiException, OverdueException, OverdueApiException {
         final Account account = accountApi.getAccountById(UUID.fromString(accountId));
         final OverdueState<Account> overdueState = overdueApi.getOverdueStateFor(account);
 
@@ -73,7 +73,7 @@ public class OverdueResource implements JaxrsResource {
     @GET
     @Path("/" + BUNDLES + "/{bundleId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
-    public Response getOverdueBundle(@PathParam("bundleId") final String bundleId) throws EntitlementUserApiException, OverdueError, OverdueApiException {
+    public Response getOverdueBundle(@PathParam("bundleId") final String bundleId) throws EntitlementUserApiException, OverdueException, OverdueApiException {
         final SubscriptionBundle bundle = entitlementApi.getBundleFromId(UUID.fromString(bundleId));
         final OverdueState<SubscriptionBundle> overdueState = overdueApi.getOverdueStateFor(bundle);
 
@@ -83,7 +83,7 @@ public class OverdueResource implements JaxrsResource {
     @GET
     @Path("/" + SUBSCRIPTIONS + "/{subscriptionId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
-    public Response getOverdueSubscription(@PathParam("subscriptionId") final String subscriptionId) throws EntitlementUserApiException, OverdueError, OverdueApiException {
+    public Response getOverdueSubscription(@PathParam("subscriptionId") final String subscriptionId) throws EntitlementUserApiException, OverdueException, OverdueApiException {
         final Subscription subscription = entitlementApi.getSubscriptionFromId(UUID.fromString(subscriptionId));
         final OverdueState<Subscription> overdueState = overdueApi.getOverdueStateFor(subscription);
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
index 68dacad..178eea9 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
@@ -220,6 +220,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
                                            @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
                                            @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion,
                                            @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("5") final long timeoutSec,
+                                           @QueryParam(QUERY_POLICY) final String policyString,
                                            @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                            @HeaderParam(HDR_REASON) final String reason,
                                            @HeaderParam(HDR_COMMENT) final String comment,
@@ -237,7 +238,12 @@ public class SubscriptionResource extends JaxRsResourceBase {
                 final Subscription current = entitlementApi.getSubscriptionFromId(uuid);
 
                 final DateTime inputDate = (requestedDate != null) ? DATE_TIME_FORMATTER.parseDateTime(requestedDate) : null;
-                isImmediateOp = current.cancel(inputDate, false, ctx);
+                if (policyString == null) {
+                    isImmediateOp = current.cancel(inputDate, ctx);
+                } else {
+                    final ActionPolicy policy = ActionPolicy.valueOf(policyString.toUpperCase());
+                    isImmediateOp = current.cancelWithPolicy(inputDate, policy, ctx);
+                }
                 return Response.status(Status.OK).build();
             }
 
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
index ddc67e8..1bf3906 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingSubscription.java
@@ -56,11 +56,18 @@ public class BlockingSubscription implements Subscription {
     }
 
     @Override
-    public boolean cancel(final DateTime requestedDate, final boolean eot, final CallContext context) throws EntitlementUserApiException {
-        return subscription.cancel(requestedDate, eot, context);
+    public boolean cancel(final DateTime requestedDate, final CallContext context) throws EntitlementUserApiException {
+        return subscription.cancel(requestedDate, context);
     }
 
     @Override
+    public boolean cancelWithPolicy(DateTime requestedDate, ActionPolicy policy, CallContext context)
+            throws EntitlementUserApiException {
+        return subscription.cancelWithPolicy(requestedDate, policy, context);
+    }
+
+
+    @Override
     public boolean uncancel(final CallContext context) throws EntitlementUserApiException {
         return subscription.uncancel(context);
     }
diff --git a/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckNotifier.java b/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckNotifier.java
index cf51e2b..c1fa16c 100644
--- a/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckNotifier.java
+++ b/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckNotifier.java
@@ -16,7 +16,6 @@
 
 package com.ning.billing.ovedue.notification;
 
-import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
@@ -77,7 +76,7 @@ public class DefaultOverdueCheckNotifier implements OverdueCheckNotifier {
                     }
 
                     final OverdueCheckNotificationKey key = (OverdueCheckNotificationKey) notificationKey;
-                    processEvent(key.getUuidKey(), eventDate);
+                    processEvent(key, eventDate);
                 } catch (IllegalArgumentException e) {
                     log.error("The key returned from the NextBillingNotificationQueue is not a valid UUID", e);
                 }
@@ -112,7 +111,7 @@ public class DefaultOverdueCheckNotifier implements OverdueCheckNotifier {
         }
     }
 
-    private void processEvent(final UUID overdueableId, final DateTime eventDateTime) {
-        listener.handleNextOverdueCheck(overdueableId);
+    private void processEvent(final OverdueCheckNotificationKey key, final DateTime eventDateTime) {
+        listener.handleNextOverdueCheck(key);
     }
 }
diff --git a/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckPoster.java b/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckPoster.java
index ad11522..5c90516 100644
--- a/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckPoster.java
+++ b/overdue/src/main/java/com/ning/billing/ovedue/notification/DefaultOverdueCheckPoster.java
@@ -50,7 +50,7 @@ public class DefaultOverdueCheckPoster implements OverdueCheckPoster {
                                                                               DefaultOverdueCheckNotifier.OVERDUE_CHECK_NOTIFIER_QUEUE);
             log.info("Queuing overdue check notification. id: {}, timestamp: {}", overdueable.getId().toString(), futureNotificationTime.toString());
 
-            checkOverdueQueue.recordFutureNotification(futureNotificationTime, null, new OverdueCheckNotificationKey(overdueable.getId()));
+            checkOverdueQueue.recordFutureNotification(futureNotificationTime, null, new OverdueCheckNotificationKey(overdueable.getId(), Blockable.Type.get(overdueable)));
         } catch (NoSuchNotificationQueue e) {
             log.error("Attempting to put items on a non-existent queue (DefaultOverdueCheck).", e);
         } catch (IOException e) {
diff --git a/overdue/src/main/java/com/ning/billing/ovedue/notification/OverdueCheckNotificationKey.java b/overdue/src/main/java/com/ning/billing/ovedue/notification/OverdueCheckNotificationKey.java
index 3483e8d..70a72d0 100644
--- a/overdue/src/main/java/com/ning/billing/ovedue/notification/OverdueCheckNotificationKey.java
+++ b/overdue/src/main/java/com/ning/billing/ovedue/notification/OverdueCheckNotificationKey.java
@@ -20,11 +20,23 @@ import java.util.UUID;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
+
+import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.util.notificationq.DefaultUUIDNotificationKey;
 
 public class OverdueCheckNotificationKey extends DefaultUUIDNotificationKey {
+
+    private final Blockable.Type type;
+
     @JsonCreator
-    public OverdueCheckNotificationKey(@JsonProperty("uuidKey") UUID uuidKey) {
+    public OverdueCheckNotificationKey(@JsonProperty("uuidKey") UUID uuidKey,
+            @JsonProperty("type") Blockable.Type type) {
         super(uuidKey);
+        this.type = type;
+    }
+
+    // Hack : We default to SubscriptionBundle which is the only one supported at the time
+    public Blockable.Type getType() {
+        return type == null ? Blockable.Type.SUBSCRIPTION_BUNDLE : type;
     }
 }
diff --git a/overdue/src/main/java/com/ning/billing/overdue/api/DefaultOverdueUserApi.java b/overdue/src/main/java/com/ning/billing/overdue/api/DefaultOverdueUserApi.java
index c659083..3e5ffa5 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/api/DefaultOverdueUserApi.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/api/DefaultOverdueUserApi.java
@@ -30,7 +30,7 @@ import com.ning.billing.overdue.OverdueState;
 import com.ning.billing.overdue.OverdueUserApi;
 import com.ning.billing.overdue.config.OverdueConfig;
 import com.ning.billing.overdue.config.api.BillingState;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 import com.ning.billing.overdue.config.api.OverdueStateSet;
 import com.ning.billing.overdue.wrapper.OverdueWrapper;
 import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
@@ -51,25 +51,25 @@ public class DefaultOverdueUserApi implements OverdueUserApi {
 
     @SuppressWarnings("unchecked")
     @Override
-    public <T extends Blockable> OverdueState<T> getOverdueStateFor(final T overdueable) throws OverdueError {
+    public <T extends Blockable> OverdueState<T> getOverdueStateFor(final T overdueable) throws OverdueException {
         try {
             final String stateName = accessApi.getBlockingStateFor(overdueable).getStateName();
             final OverdueStateSet<SubscriptionBundle> states = overdueConfig.getBundleStateSet();
             return (OverdueState<T>) states.findState(stateName);
         } catch (OverdueApiException e) {
-            throw new OverdueError(e, ErrorCode.OVERDUE_CAT_ERROR_ENCOUNTERED, overdueable.getId(), overdueable.getClass().getSimpleName());
+            throw new OverdueException(e, ErrorCode.OVERDUE_CAT_ERROR_ENCOUNTERED, overdueable.getId(), overdueable.getClass().getSimpleName());
         }
     }
 
     @Override
-    public <T extends Blockable> BillingState<T> getBillingStateFor(final T overdueable) throws OverdueError {
+    public <T extends Blockable> BillingState<T> getBillingStateFor(final T overdueable) throws OverdueException {
         log.info(String.format("Billing state of of %s requested", overdueable.getId()));
         final OverdueWrapper<T> wrapper = factory.createOverdueWrapperFor(overdueable);
         return wrapper.billingState();
     }
 
     @Override
-    public <T extends Blockable> OverdueState<T> refreshOverdueStateFor(final T blockable) throws OverdueError, OverdueApiException {
+    public <T extends Blockable> OverdueState<T> refreshOverdueStateFor(final T blockable) throws OverdueException, OverdueApiException {
         log.info(String.format("Refresh of %s requested", blockable.getId()));
         final OverdueWrapper<T> wrapper = factory.createOverdueWrapperFor(blockable);
         return wrapper.refresh();
diff --git a/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
index 86c9a72..40a93bd 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
@@ -16,64 +16,105 @@
 
 package com.ning.billing.overdue.applicator;
 
+import java.util.LinkedList;
+import java.util.List;
+
 import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
 import org.joda.time.Period;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.catalog.api.ActionPolicy;
+import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.junction.api.BlockingApi;
 import com.ning.billing.junction.api.BlockingApiException;
 import com.ning.billing.junction.api.DefaultBlockingState;
 import com.ning.billing.ovedue.notification.OverdueCheckPoster;
 import com.ning.billing.overdue.OverdueApiException;
+import com.ning.billing.overdue.OverdueCancellationPolicicy;
 import com.ning.billing.overdue.OverdueChangeEvent;
 import com.ning.billing.overdue.OverdueService;
 import com.ning.billing.overdue.OverdueState;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.BillingState;
+import com.ning.billing.overdue.config.api.OverdueException;
 import com.ning.billing.util.bus.Bus;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextFactory;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.clock.Clock;
 
 public class OverdueStateApplicator<T extends Blockable> {
+
+    private static final String API_USER_NAME = "OverdueStateApplicator";
+
     private static final Logger log = LoggerFactory.getLogger(OverdueStateApplicator.class);
 
     private final BlockingApi blockingApi;
     private final Clock clock;
     private final OverdueCheckPoster poster;
     private final Bus bus;
+    private final EntitlementUserApi entitlementUserApi;
+    private final CallContextFactory factory;
 
 
     @Inject
-    public OverdueStateApplicator(final BlockingApi accessApi, final Clock clock, final OverdueCheckPoster poster, final Bus bus) {
+    public OverdueStateApplicator(final BlockingApi accessApi, final EntitlementUserApi entitlementUserApi, final Clock clock,
+            final OverdueCheckPoster poster, final Bus bus, final CallContextFactory factory) {
         this.blockingApi = accessApi;
+        this.entitlementUserApi = entitlementUserApi;
         this.clock = clock;
         this.poster = poster;
         this.bus = bus;
+        this.factory = factory;
     }
 
-    public void apply(final T overdueable, final String previousOverdueStateName, final OverdueState<T> nextOverdueState) throws OverdueError {
-        if (previousOverdueStateName.equals(nextOverdueState.getName())) {
-            return; // nothing to do
-        }
 
-        storeNewState(overdueable, nextOverdueState);
+    public void apply(final OverdueState<T> firstOverdueState, final BillingState<T> billingState,
+            final T overdueable, final String previousOverdueStateName, final OverdueState<T> nextOverdueState) throws OverdueException {
+
         try {
+
+            // We did not reach first state, we we need to check if there is any pending condition for which we will not receive
+            // any notifications.. (last two conditions are there for test purpose)
+            if (nextOverdueState.isClearState() && firstOverdueState != null && billingState !=  null) {
+                 final LocalDate firstUnpaidInvoice = billingState.getDateOfEarliestUnpaidInvoice();
+                 if (firstUnpaidInvoice != null) {
+                     final Period reevaluationInterval = firstOverdueState.getReevaluationInterval();
+                     createFutureNotification(overdueable, firstUnpaidInvoice.toDateTimeAtCurrentTime().plus(reevaluationInterval));
+                 }
+            }
+
+            if (nextOverdueState == null || previousOverdueStateName.equals(nextOverdueState.getName())) {
+                return; //That's it we are done...
+            }
+
+            storeNewState(overdueable, nextOverdueState);
+
+            cancelSubscriptionsIfRequired(overdueable, nextOverdueState);
+
             final Period reevaluationInterval = nextOverdueState.getReevaluationInterval();
             if (!nextOverdueState.isClearState()) {
                 createFutureNotification(overdueable, clock.getUTCNow().plus(reevaluationInterval));
             }
         } catch (OverdueApiException e) {
             if (e.getCode() != ErrorCode.OVERDUE_NO_REEVALUATION_INTERVAL.getCode()) {
-                throw new OverdueError(e);
+                throw new OverdueException(e);
             }
         }
 
         if (nextOverdueState.isClearState()) {
             clear(overdueable);
         }
-        
+
         try {
             bus.post(createOverdueEvent(overdueable, previousOverdueStateName, nextOverdueState.getName()));
         } catch (Exception e) {
@@ -86,12 +127,12 @@ public class OverdueStateApplicator<T extends Blockable> {
         return new DefaultOverdueChangeEvent(overdueable.getId(), Blockable.Type.get(overdueable), previousOverdueStateName, nextOverdueStateName, null);
     }
 
-    protected void storeNewState(final T blockable, final OverdueState<T> nextOverdueState) throws OverdueError {
+    protected void storeNewState(final T blockable, final OverdueState<T> nextOverdueState) throws OverdueException {
         try {
             blockingApi.setBlockingState(new DefaultBlockingState(blockable.getId(), nextOverdueState.getName(), Blockable.Type.get(blockable),
                                                                   OverdueService.OVERDUE_SERVICE_NAME, blockChanges(nextOverdueState), blockEntitlement(nextOverdueState), blockBilling(nextOverdueState)));
         } catch (Exception e) {
-            throw new OverdueError(e, ErrorCode.OVERDUE_CAT_ERROR_ENCOUNTERED, blockable.getId(), blockable.getClass().getName());
+            throw new OverdueException(e, ErrorCode.OVERDUE_CAT_ERROR_ENCOUNTERED, blockable.getId(), blockable.getClass().getName());
         }
     }
 
@@ -118,4 +159,46 @@ public class OverdueStateApplicator<T extends Blockable> {
         poster.clearNotificationsFor(blockable);
     }
 
+    private void cancelSubscriptionsIfRequired(final T blockable, final OverdueState<T> nextOverdueState) throws OverdueException {
+        if (nextOverdueState.getSubscriptionCancellationPolicy() == OverdueCancellationPolicicy.NONE) {
+            return;
+        }
+        try {
+            ActionPolicy actionPolicy = null;
+            switch(nextOverdueState.getSubscriptionCancellationPolicy()) {
+            case END_OF_TERM:
+                actionPolicy = ActionPolicy.END_OF_TERM;
+                break;
+            case IMMEDIATE:
+                actionPolicy = ActionPolicy.IMMEDIATE;
+                break;
+            default :
+                throw new IllegalStateException("Unexpected OverdueCancellationPolicy " + nextOverdueState.getSubscriptionCancellationPolicy());
+            }
+            final List<Subscription> toBeCancelled = new LinkedList<Subscription>();
+            computeSubscriptionsToCancel(blockable, toBeCancelled);
+            final CallContext context = factory.createCallContext(API_USER_NAME, CallOrigin.INTERNAL, UserType.SYSTEM);
+            for (Subscription cur : toBeCancelled) {
+                cur.cancelWithPolicy(clock.getUTCNow(), actionPolicy, context);
+            }
+        } catch (EntitlementUserApiException e) {
+            throw new OverdueException(e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void computeSubscriptionsToCancel(final T blockable, final List<Subscription> result) throws EntitlementUserApiException{
+        if (blockable instanceof Subscription) {
+            result.add((Subscription) blockable);
+            return;
+        } else if (blockable instanceof SubscriptionBundle) {
+            for (Subscription cur : entitlementUserApi.getSubscriptionsForBundle(blockable.getId())) {
+                computeSubscriptionsToCancel((T) cur, result);
+            }
+        } else if (blockable instanceof Account) {
+            for (SubscriptionBundle cur : entitlementUserApi.getBundlesForAccount(blockable.getId())) {
+                computeSubscriptionsToCancel((T) cur, result);
+            }
+        }
+    }
 }
diff --git a/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculator.java b/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculator.java
index 8346357..feb0bf4 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculator.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculator.java
@@ -31,7 +31,7 @@ import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.overdue.config.api.BillingState;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 import com.ning.billing.util.clock.Clock;
 
 import com.google.inject.Inject;
@@ -60,7 +60,7 @@ public abstract class BillingStateCalculator<T extends Blockable> {
         this.clock = clock;
     }
 
-    public abstract BillingState<T> calculateBillingState(T overdueable) throws OverdueError;
+    public abstract BillingState<T> calculateBillingState(T overdueable) throws OverdueException;
 
     protected Invoice earliest(final SortedSet<Invoice> unpaidInvoices) {
         try {
diff --git a/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculatorBundle.java b/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculatorBundle.java
index a72ca90..3a5b607 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculatorBundle.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/calculator/BillingStateCalculatorBundle.java
@@ -42,7 +42,7 @@ import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.overdue.config.api.BillingStateBundle;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 import com.ning.billing.overdue.config.api.PaymentResponse;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.tag.Tag;
@@ -61,7 +61,7 @@ public class BillingStateCalculatorBundle extends BillingStateCalculator<Subscri
     }
 
     @Override
-    public BillingStateBundle calculateBillingState(final SubscriptionBundle bundle) throws OverdueError {
+    public BillingStateBundle calculateBillingState(final SubscriptionBundle bundle) throws OverdueException {
         try {
             final Account account = accountApi.getAccountById(bundle.getAccountId());
             final SortedSet<Invoice> unpaidInvoices = unpaidInvoicesForBundle(bundle.getId(), bundle.getAccountId(), account.getTimeZone());
@@ -111,9 +111,9 @@ public class BillingStateCalculatorBundle extends BillingStateCalculator<Subscri
                                           basePlanPriceList,
                                           basePlanPhaseType);
         } catch (EntitlementUserApiException e) {
-            throw new OverdueError(e);
+            throw new OverdueException(e);
         } catch (AccountApiException e) {
-            throw new OverdueError(e);
+            throw new OverdueException(e);
         }
     }
 
diff --git a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultCondition.java b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultCondition.java
index cd50142..359beaa 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultCondition.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultCondition.java
@@ -24,7 +24,9 @@ import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
 
+import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
+import org.joda.time.Period;
 
 import com.ning.billing.catalog.api.Duration;
 import com.ning.billing.catalog.api.TimeUnit;
diff --git a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java
index cc4e859..383b3fd 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java
@@ -28,6 +28,7 @@ import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.TimeUnit;
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.overdue.OverdueApiException;
+import com.ning.billing.overdue.OverdueCancellationPolicicy;
 import com.ning.billing.overdue.OverdueState;
 import com.ning.billing.util.config.ValidatingConfig;
 import com.ning.billing.util.config.ValidationError;
@@ -38,6 +39,7 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
 
     private static final int MAX_NAME_LENGTH = 50;
 
+
     @XmlElement(required = false, name = "condition")
     private DefaultCondition<T> condition;
 
@@ -54,6 +56,9 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
     @XmlElement(required = false, name = "disableEntitlementAndChangesBlocked")
     private Boolean disableEntitlement = false;
 
+    @XmlElement(required = false, name = "subscriptionCancellationPolicy")
+    private OverdueCancellationPolicicy subscriptionCancellationPolicy = OverdueCancellationPolicicy.NONE;
+
     @XmlElement(required = false, name = "isClearState")
     private Boolean isClearState = false;
 
@@ -99,6 +104,11 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
     }
 
     @Override
+    public OverdueCancellationPolicicy getSubscriptionCancellationPolicy() {
+        return subscriptionCancellationPolicy;
+    }
+
+    @Override
     public Period getReevaluationInterval() throws OverdueApiException {
         if (autoReevaluationInterval == null || autoReevaluationInterval.getUnit() == TimeUnit.UNLIMITED || autoReevaluationInterval.getNumber() == 0) {
             throw new OverdueApiException(ErrorCode.OVERDUE_NO_REEVALUATION_INTERVAL, name);
@@ -106,6 +116,7 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
         return autoReevaluationInterval.toJodaPeriod();
     }
 
+    @Override
     public DefaultCondition<T> getCondition() {
         return condition;
     }
@@ -130,6 +141,12 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
         return this;
     }
 
+    public DefaultOverdueState<T> setSubscriptionCancellationPolicy(final OverdueCancellationPolicicy policy) {
+        this.subscriptionCancellationPolicy = policy;
+        return this;
+    }
+
+
     protected DefaultOverdueState<T> setBlockChanges(final boolean cancel) {
         this.blockChanges = cancel;
         return this;
@@ -159,6 +176,4 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
         final Integer daysBetweenPaymentRetries = 8;
         return daysBetweenPaymentRetries;
     }
-
-
 }
diff --git a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueStateSet.java b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueStateSet.java
index 8b3419a..263a8e1 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueStateSet.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueStateSet.java
@@ -19,7 +19,6 @@ package com.ning.billing.overdue.config;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 
-import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.joda.time.Period;
 
@@ -95,4 +94,8 @@ public abstract class DefaultOverdueStateSet<T extends Blockable> extends Valida
         return getStates().length;
     }
 
+    @Override
+    public OverdueState<T> getFirstState() {
+        return getStates()[0];
+    }
 }
diff --git a/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueDispatcher.java b/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueDispatcher.java
index a94448f..e7dcda8 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueDispatcher.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueDispatcher.java
@@ -28,7 +28,7 @@ import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.overdue.OverdueApiException;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
 
 public class OverdueDispatcher {
@@ -64,17 +64,17 @@ public class OverdueDispatcher {
     public void processOverdue(final Blockable blockable) {
         try {
             factory.createOverdueWrapperFor(blockable).refresh();
-        } catch (OverdueError e) {
+        } catch (OverdueException e) {
             log.error("Error processing Overdue for Blockable with id: " + blockable.getId().toString(), e);
         } catch (OverdueApiException e) {
             log.error("Error processing Overdue for Blockable with id: " + blockable.getId().toString(), e);
         }
     }
 
-    public void processOverdue(final UUID blockableId) {
+    public void processOverdue(final Blockable.Type type, final UUID blockableId) {
         try {
-            factory.createOverdueWrapperFor(blockableId).refresh();
-        } catch (OverdueError e) {
+            factory.createOverdueWrapperFor(type, blockableId).refresh();
+        } catch (OverdueException e) {
             log.error("Error processing Overdue for Blockable with id: " + blockableId.toString(), e);
         } catch (OverdueApiException e) {
             log.error("Error processing Overdue for Blockable with id: " + blockableId.toString(), e);
diff --git a/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueListener.java b/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueListener.java
index b4688b5..b9c7c89 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueListener.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/listener/OverdueListener.java
@@ -22,6 +22,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.ning.billing.invoice.api.InvoiceAdjustmentEvent;
+import com.ning.billing.ovedue.notification.OverdueCheckNotificationKey;
 import com.ning.billing.payment.api.PaymentErrorEvent;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 
@@ -59,8 +60,9 @@ public class OverdueListener {
         dispatcher.processOverdueForAccount(accountId);
     }
 
-    public void handleNextOverdueCheck(final UUID overdueableId) {
-        log.info(String.format("Received OD checkup notification for %s", overdueableId));
-        dispatcher.processOverdue(overdueableId);
+    public void handleNextOverdueCheck(final OverdueCheckNotificationKey notificationKey) {
+        log.info(String.format("Received OD checkup notification for type = %s, id = %s",
+                notificationKey.getType(), notificationKey.getUuidKey()));
+        dispatcher.processOverdue(notificationKey.getType(), notificationKey.getUuidKey());
     }
 }
diff --git a/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java b/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java
index 5ed1d52..e7330ac 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java
@@ -23,7 +23,7 @@ import com.ning.billing.overdue.OverdueState;
 import com.ning.billing.overdue.applicator.OverdueStateApplicator;
 import com.ning.billing.overdue.calculator.BillingStateCalculator;
 import com.ning.billing.overdue.config.api.BillingState;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 import com.ning.billing.overdue.config.api.OverdueStateSet;
 import com.ning.billing.util.clock.Clock;
 
@@ -48,7 +48,7 @@ public class OverdueWrapper<T extends Blockable> {
         this.overdueStateApplicator = overdueStateApplicator;
     }
 
-    public OverdueState<T> refresh() throws OverdueError, OverdueApiException {
+    public OverdueState<T> refresh() throws OverdueException, OverdueApiException {
         if (overdueStateSet.size() < 1) { // No configuration available
             return overdueStateSet.getClearState();
         }
@@ -57,14 +57,12 @@ public class OverdueWrapper<T extends Blockable> {
         final String previousOverdueStateName = api.getBlockingStateFor(overdueable).getStateName();
         final OverdueState<T> nextOverdueState = overdueStateSet.calculateOverdueState(billingState, clock.getToday(billingState.getAccountTimeZone()));
 
-        if (nextOverdueState != null && !previousOverdueStateName.equals(nextOverdueState.getName())) {
-            overdueStateApplicator.apply(overdueable, previousOverdueStateName, nextOverdueState);
-        }
+        overdueStateApplicator.apply(overdueStateSet.getFirstState(), billingState, overdueable, previousOverdueStateName, nextOverdueState);
 
         return nextOverdueState;
     }
 
-    public BillingState<T> billingState() throws OverdueError {
+    public BillingState<T> billingState() throws OverdueException {
         return billingStateCalcuator.calculateBillingState(overdueable);
     }
 }
diff --git a/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapperFactory.java b/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapperFactory.java
index aed2aad..c410e28 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapperFactory.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapperFactory.java
@@ -34,7 +34,7 @@ import com.ning.billing.overdue.calculator.BillingStateCalculatorBundle;
 import com.ning.billing.overdue.config.DefaultOverdueState;
 import com.ning.billing.overdue.config.DefaultOverdueStateSet;
 import com.ning.billing.overdue.config.OverdueConfig;
-import com.ning.billing.overdue.config.api.OverdueError;
+import com.ning.billing.overdue.config.api.OverdueException;
 import com.ning.billing.overdue.config.api.OverdueStateSet;
 import com.ning.billing.util.clock.Clock;
 
@@ -61,34 +61,32 @@ public class OverdueWrapperFactory {
     }
 
     @SuppressWarnings("unchecked")
-    public <T extends Blockable> OverdueWrapper<T> createOverdueWrapperFor(final T bloackable) throws OverdueError {
+    public <T extends Blockable> OverdueWrapper<T> createOverdueWrapperFor(final T bloackable) throws OverdueException {
 
         if (bloackable instanceof SubscriptionBundle) {
             return (OverdueWrapper<T>) new OverdueWrapper<SubscriptionBundle>((SubscriptionBundle) bloackable, api, getOverdueStateSetBundle(),
                                                                               clock, billingStateCalcuatorBundle, overdueStateApplicatorBundle);
         } else {
-            throw new OverdueError(ErrorCode.OVERDUE_TYPE_NOT_SUPPORTED, bloackable.getId(), bloackable.getClass());
+            throw new OverdueException(ErrorCode.OVERDUE_TYPE_NOT_SUPPORTED, bloackable.getId(), bloackable.getClass());
         }
     }
 
     @SuppressWarnings("unchecked")
-    public <T extends Blockable> OverdueWrapper<T> createOverdueWrapperFor(final UUID id) throws OverdueError {
-        final BlockingState state = api.getBlockingStateFor(id);
-
+    public <T extends Blockable> OverdueWrapper<T> createOverdueWrapperFor(final Blockable.Type type, final UUID id) throws OverdueException {
         try {
-            switch (state.getType()) {
+            switch (type) {
                 case SUBSCRIPTION_BUNDLE: {
                     final SubscriptionBundle bundle = entitlementApi.getBundleFromId(id);
                     return (OverdueWrapper<T>) new OverdueWrapper<SubscriptionBundle>(bundle, api, getOverdueStateSetBundle(),
                                                                                       clock, billingStateCalcuatorBundle, overdueStateApplicatorBundle);
                 }
                 default: {
-                    throw new OverdueError(ErrorCode.OVERDUE_TYPE_NOT_SUPPORTED, id, state.getType());
+                    throw new OverdueException(ErrorCode.OVERDUE_TYPE_NOT_SUPPORTED, id, type);
                 }
 
             }
         } catch (EntitlementUserApiException e) {
-            throw new OverdueError(e);
+            throw new OverdueException(e);
         }
     }
 
diff --git a/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java b/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java
index 87347a3..067b2c1 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java
@@ -63,17 +63,17 @@ public class TestOverdueStateApplicator extends OverdueTestBase {
         OverdueState<SubscriptionBundle> state;
 
         state = config.getBundleStateSet().findState("OD1");
-        applicator.apply(bundle, BlockingApi.CLEAR_STATE_NAME, state);
+        applicator.apply(null, null, bundle, BlockingApi.CLEAR_STATE_NAME, state);
         checkStateApplied(state);
         checkBussEvent("OD1");
 
         state = config.getBundleStateSet().findState("OD2");
-        applicator.apply(bundle, BlockingApi.CLEAR_STATE_NAME, state);
+        applicator.apply(null, null,bundle, BlockingApi.CLEAR_STATE_NAME, state);
         checkStateApplied(state);
         checkBussEvent("OD2");
 
         state = config.getBundleStateSet().findState("OD3");
-        applicator.apply(bundle, BlockingApi.CLEAR_STATE_NAME, state);
+        applicator.apply(null, null, bundle, BlockingApi.CLEAR_STATE_NAME, state);
         checkStateApplied(state);
         checkBussEvent("OD3");
         bus.stop();
diff --git a/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java b/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java
index 5965e02..d2cecf1 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java
@@ -50,6 +50,7 @@ import com.ning.billing.mock.glue.MockJunctionModule;
 import com.ning.billing.mock.glue.MockPaymentModule;
 import com.ning.billing.ovedue.notification.DefaultOverdueCheckNotifier;
 import com.ning.billing.ovedue.notification.DefaultOverdueCheckPoster;
+import com.ning.billing.ovedue.notification.OverdueCheckNotificationKey;
 import com.ning.billing.ovedue.notification.OverdueCheckPoster;
 import com.ning.billing.overdue.OverdueProperties;
 import com.ning.billing.overdue.OverdueTestSuiteWithEmbeddedDB;
@@ -92,9 +93,9 @@ public class TestOverdueCheckNotifier extends OverdueTestSuiteWithEmbeddedDB {
         }
 
         @Override
-        public void handleNextOverdueCheck(final UUID subscriptionId) {
+        public void handleNextOverdueCheck(final OverdueCheckNotificationKey key) {
             eventCount++;
-            latestSubscriptionId = subscriptionId;
+            latestSubscriptionId = key.getUuidKey();
         }
 
         public int getEventCount() {
@@ -109,6 +110,7 @@ public class TestOverdueCheckNotifier extends OverdueTestSuiteWithEmbeddedDB {
     @BeforeClass(groups = "slow")
     public void setup() throws ServiceException, IOException, ClassNotFoundException, SQLException, EntitlementUserApiException {
         final Injector g = Guice.createInjector(Stage.PRODUCTION, new MockInvoiceModule(), new MockPaymentModule(), new BusModule(), new DefaultOverdueModule() {
+            @Override
             protected void configure() {
                 super.configure();
                 bind(Clock.class).to(ClockMock.class).asEagerSingleton();
diff --git a/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueNotificationKeyJson.java b/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueNotificationKeyJson.java
new file mode 100644
index 0000000..183ffae
--- /dev/null
+++ b/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueNotificationKeyJson.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.overdue.notification;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.UUID;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.junction.api.Blockable;
+import com.ning.billing.ovedue.notification.OverdueCheckNotificationKey;
+import com.ning.billing.util.jackson.ObjectMapper;
+
+public class TestOverdueNotificationKeyJson {
+
+
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    @Test(groups = "fast")
+    public void testOverdueNotificationKeyJson() throws Exception {
+
+        final UUID uuid = UUID.randomUUID();
+        final Blockable.Type type  = Blockable.Type.SUBSCRIPTION;
+
+        final OverdueCheckNotificationKey e = new OverdueCheckNotificationKey(uuid, type);
+
+        final String json = mapper.writeValueAsString(e);
+
+        final Class<?> claz = Class.forName(OverdueCheckNotificationKey.class.getName());
+        final Object obj = mapper.readValue(json, claz);
+        Assert.assertTrue(obj.equals(e));
+    }
+
+    @Test(groups = "fast")
+    public void testOverdueNotificationKeyJsonWithNoKey() throws Exception {
+
+        final String uuidString = "bab0fca4-c628-4997-8980-14d6c3a98c48";
+        final String json = "{\"uuidKey\":\"" + uuidString +  "\"}";
+
+        final Class<?> claz = Class.forName(OverdueCheckNotificationKey.class.getName());
+        final OverdueCheckNotificationKey obj = (OverdueCheckNotificationKey) mapper.readValue(json, claz);
+        assertEquals(obj.getUuidKey().toString(), uuidString);
+        assertEquals(obj.getType(), Blockable.Type.SUBSCRIPTION_BUNDLE);
+    }
+}
diff --git a/overdue/src/test/java/com/ning/billing/overdue/OverdueTestBase.java b/overdue/src/test/java/com/ning/billing/overdue/OverdueTestBase.java
index 45a5626..6b8d67e 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/OverdueTestBase.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/OverdueTestBase.java
@@ -60,10 +60,11 @@ import com.ning.billing.overdue.service.DefaultOverdueService;
 import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
 import com.ning.billing.util.bus.BusService;
 import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.glue.CallContextModule;
 import com.ning.billing.util.glue.NotificationQueueModule;
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueAlreadyExists;
 
-@Guice(modules = {DefaultOverdueModule.class, OverdueListenerTesterModule.class, MockClockModule.class, ApplicatorMockJunctionModule.class, CatalogModule.class, MockInvoiceModule.class, MockPaymentModule.class, NotificationQueueModule.class, TestDbiModule.class})
+@Guice(modules = {DefaultOverdueModule.class, OverdueListenerTesterModule.class, MockClockModule.class, ApplicatorMockJunctionModule.class, CallContextModule.class, CatalogModule.class, MockInvoiceModule.class, MockPaymentModule.class, NotificationQueueModule.class, TestDbiModule.class})
 public abstract class OverdueTestBase extends OverdueTestSuiteWithEmbeddedDB {
     protected final String configXml =
             "<overdueConfig>" +
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index 75e6092..0426343 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -37,6 +37,7 @@ import javax.ws.rs.core.Response.Status;
 
 import org.eclipse.jetty.servlet.FilterHolder;
 import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
 import org.skife.config.ConfigurationObjectFactory;
 import org.skife.jdbi.v2.IDBI;
 import org.slf4j.Logger;
@@ -279,6 +280,8 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
     @BeforeMethod(groups = "slow")
     public void cleanupBeforeMethod(final Method method) {
         busHandler.reset();
+        clock.reset();
+        clock.setDay(new LocalDate(2012, 8, 25));
     }
 
     @BeforeClass(groups = "slow")
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 1425500..0ff6407 100644
--- a/util/src/test/java/com/ning/billing/mock/MockSubscription.java
+++ b/util/src/test/java/com/ning/billing/mock/MockSubscription.java
@@ -70,8 +70,15 @@ public class MockSubscription implements Subscription {
     Subscription sub = Mockito.mock(Subscription.class);
 
     @Override
-    public boolean cancel(final DateTime requestedDate, final boolean eot, final CallContext context) throws EntitlementUserApiException {
-        return sub.cancel(requestedDate, eot, context);
+    public boolean cancel(final DateTime requestedDate, final CallContext context) throws EntitlementUserApiException {
+        return sub.cancel(requestedDate, context);
+    }
+
+    @Override
+    public boolean cancelWithPolicy(DateTime requestedDate,
+            ActionPolicy policy, CallContext context)
+            throws EntitlementUserApiException {
+        return sub.cancelWithPolicy(requestedDate, policy, context);
     }
 
     @Override