killbill-memoizeit

Changes

NEWS 11(+11 -0)

Details

diff --git a/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBase.java b/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBase.java
index 09fabfc..7675239 100644
--- a/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBase.java
+++ b/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBase.java
@@ -25,7 +25,6 @@ import com.ning.billing.catalog.api.BillingActionPolicy;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
-import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PriceList;
 import com.ning.billing.catalog.api.Product;
 import com.ning.billing.catalog.api.ProductCategory;
@@ -34,13 +33,11 @@ import com.ning.billing.entitlement.api.Entitlement.EntitlementSourceType;
 import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
 import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
 import com.ning.billing.subscription.api.user.SubscriptionBaseTransition;
-import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.entity.Entity;
 
 public interface SubscriptionBase extends Entity, Blockable {
 
-
     public boolean cancel(final CallContext context)
             throws SubscriptionBaseApiException;
 
@@ -98,8 +95,6 @@ public interface SubscriptionBase extends Entity, Blockable {
 
     public DateTime getChargedThroughDate();
 
-    public DateTime getPaidThroughDate();
-
     public ProductCategory getCategory();
 
     public SubscriptionBaseTransition getPendingTransition();
diff --git a/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseInternalApi.java b/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseInternalApi.java
index b8de4a1..02a1bd9 100644
--- a/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseInternalApi.java
+++ b/api/src/main/java/com/ning/billing/subscription/api/SubscriptionBaseInternalApi.java
@@ -49,7 +49,7 @@ public interface SubscriptionBaseInternalApi {
 
     public List<SubscriptionBaseBundle> getBundlesForKey(final String bundleKey, final InternalTenantContext context);
 
-    public SubscriptionBaseBundle getActiveBundleForKey(final String bundleKey, final InternalTenantContext context) throws SubscriptionBaseApiException;
+    public Iterable<UUID> getNonAOSubscriptionIdsForKey(final String bundleKey, final InternalTenantContext context);
 
     public List<SubscriptionBase> getSubscriptionsForBundle(final UUID bundleId, final InternalTenantContext context);
 
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
index a1b3deb..4481d3a 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
@@ -44,6 +44,7 @@ import com.ning.billing.entitlement.DefaultEntitlementService;
 import com.ning.billing.entitlement.EntitlementService;
 import com.ning.billing.entitlement.EntitlementTransitionType;
 import com.ning.billing.entitlement.EventsStream;
+import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
 import com.ning.billing.entitlement.block.BlockingChecker;
 import com.ning.billing.entitlement.dao.BlockingStateDao;
 import com.ning.billing.entitlement.engine.core.EntitlementNotificationKey;
@@ -79,8 +80,8 @@ public class DefaultEntitlementApi implements EntitlementApi {
     public static final String ENT_STATE_CLEAR = "ENT_CLEAR";
     public static final String ENT_STATE_CANCELLED = "ENT_CANCELLED";
 
-    private final SubscriptionBaseInternalApi subscriptionInternalApi;
-    private final SubscriptionBaseTransferApi subscriptionTransferApi;
+    private final SubscriptionBaseInternalApi subscriptionBaseInternalApi;
+    private final SubscriptionBaseTransferApi subscriptionBaseTransferApi;
     private final AccountInternalApi accountApi;
     private final Clock clock;
     private final InternalCallContextFactory internalCallContextFactory;
@@ -100,8 +101,8 @@ public class DefaultEntitlementApi implements EntitlementApi {
                                  final EventsStreamBuilder eventsStreamBuilder, final EntitlementUtils entitlementUtils) {
         this.eventBus = eventBus;
         this.internalCallContextFactory = internalCallContextFactory;
-        this.subscriptionInternalApi = subscriptionInternalApi;
-        this.subscriptionTransferApi = subscriptionTransferApi;
+        this.subscriptionBaseInternalApi = subscriptionInternalApi;
+        this.subscriptionBaseTransferApi = subscriptionTransferApi;
         this.accountApi = accountApi;
         this.clock = clock;
         this.checker = checker;
@@ -116,14 +117,19 @@ public class DefaultEntitlementApi implements EntitlementApi {
     public Entitlement createBaseEntitlement(final UUID accountId, final PlanPhaseSpecifier planPhaseSpecifier, final String externalKey, final LocalDate effectiveDate, final CallContext callContext) throws EntitlementApiException {
         final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(accountId, callContext);
         try {
-            final SubscriptionBaseBundle bundle = subscriptionInternalApi.createBundleForAccount(accountId, externalKey, contextWithValidAccountRecordId);
+
+            if (entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, contextWithValidAccountRecordId) != null) {
+                throw new EntitlementApiException(new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS, externalKey));
+            }
+
+            final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.createBundleForAccount(accountId, externalKey, contextWithValidAccountRecordId);
 
             final DateTime referenceTime = clock.getUTCNow();
             final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(effectiveDate, referenceTime, contextWithValidAccountRecordId);
-            final SubscriptionBase subscription = subscriptionInternalApi.createSubscription(bundle.getId(), planPhaseSpecifier, requestedDate, contextWithValidAccountRecordId);
+            final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(bundle.getId(), planPhaseSpecifier, requestedDate, contextWithValidAccountRecordId);
 
             return new DefaultEntitlement(subscription.getId(), eventsStreamBuilder, this,
-                                          blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
+                                          blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
                                           entitlementUtils, dateHelper, clock, internalCallContextFactory, callContext);
         } catch (SubscriptionBaseApiException e) {
             throw new EntitlementApiException(e);
@@ -148,10 +154,10 @@ public class DefaultEntitlementApi implements EntitlementApi {
 
         try {
             final InternalCallContext context = internalCallContextFactory.createInternalCallContext(callContext);
-            final SubscriptionBase subscription = subscriptionInternalApi.createSubscription(bundleId, planPhaseSpecifier, requestedDate, context);
+            final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(bundleId, planPhaseSpecifier, requestedDate, context);
 
             return new DefaultEntitlement(subscription.getId(), eventsStreamBuilder, this,
-                                          blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
+                                          blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
                                           entitlementUtils, dateHelper, clock, internalCallContextFactory, callContext);
         } catch (SubscriptionBaseApiException e) {
             throw new EntitlementApiException(e);
@@ -162,12 +168,12 @@ public class DefaultEntitlementApi implements EntitlementApi {
     public List<EntitlementAOStatusDryRun> getDryRunStatusForChange(final UUID bundleId, final String targetProductName, final LocalDate effectiveDate, final TenantContext context) throws EntitlementApiException {
         final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
         try {
-            final SubscriptionBaseBundle bundle = subscriptionInternalApi.getBundleFromId(bundleId, internalContext);
-            final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundleId, internalContext);
+            final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getBundleFromId(bundleId, internalContext);
+            final SubscriptionBase baseSubscription = subscriptionBaseInternalApi.getBaseSubscription(bundleId, internalContext);
 
             final InternalTenantContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(bundle.getAccountId(), context);
             final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(effectiveDate, baseSubscription.getStartDate(), contextWithValidAccountRecordId);
-            return subscriptionInternalApi.getDryRunChangePlanStatus(baseSubscription.getId(), targetProductName, requestedDate, contextWithValidAccountRecordId);
+            return subscriptionBaseInternalApi.getDryRunChangePlanStatus(baseSubscription.getId(), targetProductName, requestedDate, contextWithValidAccountRecordId);
         } catch (SubscriptionBaseApiException e) {
             throw new EntitlementApiException(e);
         }
@@ -177,7 +183,7 @@ public class DefaultEntitlementApi implements EntitlementApi {
     public Entitlement getEntitlementForId(final UUID uuid, final TenantContext tenantContext) throws EntitlementApiException {
         final EventsStream eventsStream = eventsStreamBuilder.buildForEntitlement(uuid, tenantContext);
         return new DefaultEntitlement(eventsStream, eventsStreamBuilder, this,
-                                      blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
+                                      blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
                                       entitlementUtils, dateHelper, clock, internalCallContextFactory);
     }
 
@@ -186,7 +192,7 @@ public class DefaultEntitlementApi implements EntitlementApi {
         final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(tenantContext);
         final UUID accountId;
         try {
-            accountId = subscriptionInternalApi.getBundleFromId(bundleId, internalContext).getAccountId();
+            accountId = subscriptionBaseInternalApi.getBundleFromId(bundleId, internalContext).getAccountId();
         } catch (SubscriptionBaseApiException e) {
             throw new EntitlementApiException(e);
         }
@@ -224,7 +230,7 @@ public class DefaultEntitlementApi implements EntitlementApi {
                                                               @Override
                                                               public Entitlement apply(final EventsStream eventsStream) {
                                                                   return new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi,
-                                                                                                blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
+                                                                                                blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
                                                                                                 entitlementUtils, dateHelper, clock, internalCallContextFactory);
                                                               }
                                                           });
@@ -239,9 +245,9 @@ public class DefaultEntitlementApi implements EntitlementApi {
                 throw new EntitlementApiException(ErrorCode.ENT_ALREADY_BLOCKED, bundleId);
             }
 
-            final SubscriptionBaseBundle bundle = subscriptionInternalApi.getBundleFromId(bundleId, contextWithValidAccountRecordId);
+            final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getBundleFromId(bundleId, contextWithValidAccountRecordId);
             final Account account = accountApi.getAccountById(bundle.getAccountId(), contextWithValidAccountRecordId);
-            final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundleId, contextWithValidAccountRecordId);
+            final SubscriptionBase baseSubscription = subscriptionBaseInternalApi.getBaseSubscription(bundleId, contextWithValidAccountRecordId);
             final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(localEffectiveDate, baseSubscription.getStartDate(), contextWithValidAccountRecordId);
 
             if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, account.getTimeZone())) {
@@ -276,9 +282,9 @@ public class DefaultEntitlementApi implements EntitlementApi {
     public void resume(final UUID bundleId, final LocalDate localEffectiveDate, final CallContext context) throws EntitlementApiException {
         try {
             final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, context);
-            final SubscriptionBaseBundle bundle = subscriptionInternalApi.getBundleFromId(bundleId, contextWithValidAccountRecordId);
+            final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getBundleFromId(bundleId, contextWithValidAccountRecordId);
             final Account account = accountApi.getAccountById(bundle.getAccountId(), contextWithValidAccountRecordId);
-            final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundleId, contextWithValidAccountRecordId);
+            final SubscriptionBase baseSubscription = subscriptionBaseInternalApi.getBaseSubscription(bundleId, contextWithValidAccountRecordId);
 
             final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(localEffectiveDate, baseSubscription.getStartDate(), contextWithValidAccountRecordId);
 
@@ -338,18 +344,23 @@ public class DefaultEntitlementApi implements EntitlementApi {
 
         final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(sourceAccountId, context);
         try {
-            final SubscriptionBaseBundle bundle = subscriptionInternalApi.getActiveBundleForKey(externalKey, contextWithValidAccountRecordId);
-            if (!bundle.getAccountId().equals(sourceAccountId)) {
+
+            final UUID activeSubscriptionIdForKey = entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, contextWithValidAccountRecordId);
+            final SubscriptionBase baseSubscription = activeSubscriptionIdForKey != null ?
+                                                      subscriptionBaseInternalApi.getSubscriptionFromId(activeSubscriptionIdForKey, contextWithValidAccountRecordId) : null;
+            final SubscriptionBaseBundle baseBundle = baseSubscription != null ?
+                                                      subscriptionBaseInternalApi.getBundleFromId(baseSubscription.getBundleId(), contextWithValidAccountRecordId) : null;
+
+            if (baseBundle == null || ! baseBundle.getAccountId().equals(sourceAccountId)) {
                 throw new EntitlementApiException(new SubscriptionBaseApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_KEY, externalKey));
             }
-            final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundle.getId(), contextWithValidAccountRecordId);
 
             final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(effectiveDate, baseSubscription.getStartDate(), contextWithValidAccountRecordId);
-            final SubscriptionBaseBundle newBundle = subscriptionTransferApi.transferBundle(sourceAccountId, destAccountId, externalKey, requestedDate, true, cancelImm, context);
+            final SubscriptionBaseBundle newBundle = subscriptionBaseTransferApi.transferBundle(sourceAccountId, destAccountId, externalKey, requestedDate, true, cancelImm, context);
 
             // Block all associated subscriptions - TODO Do we want to block the bundle as well (this will add an extra STOP_ENTITLEMENT event in the bundle timeline stream)?
             // Note that there is no un-transfer at the moment, so we effectively add a blocking state on disk for all subscriptions
-            for (final SubscriptionBase subscriptionBase : subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), contextWithValidAccountRecordId)) {
+            for (final SubscriptionBase subscriptionBase : subscriptionBaseInternalApi.getSubscriptionsForBundle(baseBundle.getId(), contextWithValidAccountRecordId)) {
                 final BlockingState blockingState = new DefaultBlockingState(subscriptionBase.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, requestedDate);
                 entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingState, contextWithValidAccountRecordId);
             }
@@ -378,4 +389,5 @@ public class DefaultEntitlementApi implements EntitlementApi {
             throw new EntitlementApiException(e, ErrorCode.__UNKNOWN_ERROR_CODE);
         }
     }
+
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
index 253525e..928930a 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -33,6 +33,10 @@ import com.ning.billing.ObjectType;
 import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.entitlement.AccountEntitlements;
 import com.ning.billing.entitlement.EntitlementInternalApi;
+import com.ning.billing.entitlement.EventsStream;
+import com.ning.billing.entitlement.api.svcs.DefaultEntitlementInternalApi;
+import com.ning.billing.entitlement.engine.core.EntitlementUtils;
+import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
 import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
@@ -70,18 +74,20 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
     };
 
     private final EntitlementInternalApi entitlementInternalApi;
-    private final SubscriptionBaseInternalApi subscriptionInternalApi;
+    private final SubscriptionBaseInternalApi subscriptionBaseInternalApi;
     private final InternalCallContextFactory internalCallContextFactory;
     private final NonEntityDao nonEntityDao;
     private final CacheControllerDispatcher cacheControllerDispatcher;
+    private final EntitlementUtils entitlementUtils;
 
     @Inject
     public DefaultSubscriptionApi(final EntitlementInternalApi entitlementInternalApi, final SubscriptionBaseInternalApi subscriptionInternalApi,
-                                  final InternalCallContextFactory internalCallContextFactory, final NonEntityDao nonEntityDao, final CacheControllerDispatcher cacheControllerDispatcher) {
+                                  final InternalCallContextFactory internalCallContextFactory, final EntitlementUtils entitlementUtils, final NonEntityDao nonEntityDao, final CacheControllerDispatcher cacheControllerDispatcher) {
         this.entitlementInternalApi = entitlementInternalApi;
-        this.subscriptionInternalApi = subscriptionInternalApi;
+        this.subscriptionBaseInternalApi = subscriptionInternalApi;
         this.internalCallContextFactory = internalCallContextFactory;
         this.nonEntityDao = nonEntityDao;
+        this.entitlementUtils = entitlementUtils;
         this.cacheControllerDispatcher = cacheControllerDispatcher;
     }
 
@@ -144,8 +150,12 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
     public SubscriptionBundle getActiveSubscriptionBundleForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
         final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
         try {
-            final SubscriptionBaseBundle baseBundle = subscriptionInternalApi.getActiveBundleForKey(externalKey, internalContext);
-            return getSubscriptionBundle(baseBundle.getId(), context);
+            final UUID activeSubscriptionIdForKey = entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, internalContext);
+            if (activeSubscriptionIdForKey == null) {
+                throw new SubscriptionApiException(new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS, externalKey));
+            }
+            final SubscriptionBase subscriptionBase = subscriptionBaseInternalApi.getSubscriptionFromId(activeSubscriptionIdForKey, internalContext);
+            return getSubscriptionBundle(subscriptionBase.getBundleId(), context);
         } catch (SubscriptionBaseApiException e) {
             throw new SubscriptionApiException(e);
         }
@@ -154,7 +164,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
     @Override
     public List<SubscriptionBundle> getSubscriptionBundlesForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
         final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
-        final List<SubscriptionBaseBundle> baseBundles = subscriptionInternalApi.getBundlesForKey(externalKey, internalContext);
+        final List<SubscriptionBaseBundle> baseBundles = subscriptionBaseInternalApi.getBundlesForKey(externalKey, internalContext);
 
         final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(baseBundles.size());
         for (final SubscriptionBaseBundle cur : baseBundles) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
index 99af794..255f8e9 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
@@ -334,6 +334,9 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                     break;
                 case PAUSE_BILLING:
                 case PAUSE_ENTITLEMENT:
+                case RESUME_ENTITLEMENT:
+                case RESUME_BILLING:
+                case SERVICE_STATE_CHANGE:
                     curTargetState.addEntitlementEvent(cur);
                     break;
                 case STOP_BILLING:
@@ -507,12 +510,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
         }
 
         // See https://github.com/killbill/killbill/issues/135
-        final String serviceName;
-        if (DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME.equals(in.getService())) {
-            serviceName = getServiceName(eventType);
-        } else {
-            serviceName = in.getService();
-        }
+        final String serviceName = getRealServiceNameForEntitlementOrExternalServiceName(in.getService(), eventType);
 
         return new DefaultSubscriptionEvent(in.getId(),
                                             entitlementId,
@@ -537,6 +535,16 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                                             accountTimeZone);
     }
 
+    private static String getRealServiceNameForEntitlementOrExternalServiceName(final String originalServiceName, final SubscriptionEventType eventType) {
+        final String serviceName;
+        if (DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME.equals(originalServiceName)) {
+            serviceName = getServiceName(eventType);
+        } else {
+            serviceName = originalServiceName;
+        }
+        return serviceName;
+    }
+
     private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
         return new DefaultSubscriptionEvent(in.getId(),
                                             in.getSubscriptionId(),
@@ -663,8 +671,22 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
         }
 
         public void addEntitlementEvent(final SubscriptionEvent e) {
-            final BlockingState converted = new DefaultBlockingState(e.getEntitlementId(), BlockingStateType.SUBSCRIPTION,
-                                                                     e.getServiceStateName(), e.getServiceName(), false, e.isBlockedEntitlement(), e.isBlockedBilling(),
+            final String serviceName = getRealServiceNameForEntitlementOrExternalServiceName(e.getServiceName(), e.getSubscriptionEventType());
+            final BlockingState lastBlockingStateForService = perServiceBlockingState.get(serviceName);
+
+            // Assume the event has no impact on changes - TODO this is wrong for SERVICE_STATE_CHANGE
+            final boolean blockChange = lastBlockingStateForService != null && lastBlockingStateForService.isBlockChange();
+            // For block entitlement or billing, override the previous state
+            final boolean blockedEntitlement = e.isBlockedEntitlement();
+            final boolean blockedBilling = e.isBlockedBilling();
+
+            final BlockingState converted = new DefaultBlockingState(e.getEntitlementId(),
+                                                                     BlockingStateType.SUBSCRIPTION,
+                                                                     e.getServiceStateName(),
+                                                                     serviceName,
+                                                                     blockChange,
+                                                                     blockedEntitlement,
+                                                                     blockedBilling,
                                                                      ((DefaultSubscriptionEvent) e).getEffectiveDateTime());
             perServiceBlockingState.put(converted.getService(), converted);
         }
@@ -724,7 +746,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                 result.add(SubscriptionEventType.PAUSE_BILLING);
             }
 
-            if (!shouldResumeEntitlement && !shouldBlockEntitlement && !shouldBlockEntitlement && !shouldBlockBilling && !fixedBlockingState.getService().equals(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME)) {
+            if (!shouldResumeEntitlement && !shouldResumeBilling && !shouldBlockEntitlement && !shouldBlockBilling && !fixedBlockingState.getService().equals(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME)) {
                 result.add(SubscriptionEventType.SERVICE_STATE_CHANGE);
             }
             return result;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EntitlementUtils.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EntitlementUtils.java
index c595673..113447c 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EntitlementUtils.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EntitlementUtils.java
@@ -17,8 +17,10 @@
 package com.ning.billing.entitlement.engine.core;
 
 import java.io.IOException;
+import java.util.List;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import org.joda.time.DateTime;
@@ -29,12 +31,17 @@ import com.ning.billing.bus.api.BusEvent;
 import com.ning.billing.bus.api.PersistentBus;
 import com.ning.billing.bus.api.PersistentBus.EventBusException;
 import com.ning.billing.callcontext.InternalCallContext;
+import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.clock.Clock;
 import com.ning.billing.entitlement.DefaultEntitlementService;
+import com.ning.billing.entitlement.EventsStream;
 import com.ning.billing.entitlement.api.BlockingApiException;
 import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
 import com.ning.billing.entitlement.api.DefaultBlockingTransitionInternalEvent;
+import com.ning.billing.entitlement.api.DefaultEntitlementApi;
+import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
+import com.ning.billing.entitlement.api.EntitlementApiException;
 import com.ning.billing.entitlement.block.BlockingChecker;
 import com.ning.billing.entitlement.block.BlockingChecker.BlockingAggregator;
 import com.ning.billing.entitlement.dao.BlockingStateDao;
@@ -42,6 +49,13 @@ import com.ning.billing.notificationq.api.NotificationEvent;
 import com.ning.billing.notificationq.api.NotificationQueue;
 import com.ning.billing.notificationq.api.NotificationQueueService;
 import com.ning.billing.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
+import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
 
 public class EntitlementUtils {
 
@@ -49,6 +63,7 @@ public class EntitlementUtils {
 
     private final BlockingStateDao dao;
     private final BlockingChecker blockingChecker;
+    private final SubscriptionBaseInternalApi subscriptionBaseInternalApi;
     private final PersistentBus eventBus;
     private final Clock clock;
     protected final NotificationQueueService notificationQueueService;
@@ -56,11 +71,13 @@ public class EntitlementUtils {
     @Inject
     public EntitlementUtils(final BlockingStateDao dao, final BlockingChecker blockingChecker,
                             final PersistentBus eventBus, final Clock clock,
+                            final SubscriptionBaseInternalApi subscriptionBaseInternalApi,
                             final NotificationQueueService notificationQueueService) {
         this.dao = dao;
         this.blockingChecker = blockingChecker;
         this.eventBus = eventBus;
         this.clock = clock;
+        this.subscriptionBaseInternalApi = subscriptionBaseInternalApi;
         this.notificationQueueService = notificationQueueService;
     }
 
@@ -81,6 +98,25 @@ public class EntitlementUtils {
         }
     }
 
+    /**
+     *
+     * @param externalKey the bundle externalKey
+     * @param tenantContext the context
+     * @return the id of the first subscription (BASE or STANDALONE) that is still active for that key
+     */
+    public UUID getFirstActiveSubscriptionIdForKeyOrNull(final String externalKey, final InternalTenantContext tenantContext)  {
+
+        final Iterable<UUID> nonAddonUUIDs = subscriptionBaseInternalApi.getNonAOSubscriptionIdsForKey(externalKey, tenantContext);
+        return Iterables.tryFind(nonAddonUUIDs, new Predicate<UUID>() {
+            @Override
+            public boolean apply(final UUID input) {
+                final BlockingState state = dao.getBlockingStateForService(input, BlockingStateType.SUBSCRIPTION, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME, tenantContext);
+                return (state == null || !state.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED));
+            }
+        }).orNull();
+    }
+
+
     private BlockingAggregator getBlockingStateFor(final UUID blockableId, final BlockingStateType type, final InternalCallContext context) {
         try {
             return blockingChecker.getBlockedStatus(blockableId, type, context);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionApi.java
index bcfd0f1..6856ec9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionApi.java
@@ -23,6 +23,7 @@ import org.joda.time.LocalDate;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.api.TestApiListener.NextEvent;
@@ -37,6 +38,8 @@ import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.audit.ChangeType;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 
 public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbeddedDB {
@@ -106,12 +109,23 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
         final List<SubscriptionBundle> bundles = subscriptionApi.getSubscriptionBundlesForExternalKey(externalKey, callContext);
         assertEquals(bundles.size(), 1);
 
+        final SubscriptionBundle activeBundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey(externalKey, callContext);
+        assertEquals(activeBundle.getId(), entitlement.getBundleId());
+
         // Cancel entitlement
         clock.addDays(3);
         testListener.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
         entitlement.cancelEntitlementWithDate(new LocalDate(clock.getUTCNow(), account.getTimeZone()), true, callContext);
         assertListenerStatus();
 
+        try {
+            subscriptionApi.getActiveSubscriptionBundleForExternalKey(externalKey, callContext);
+            Assert.fail("Expected getActiveSubscriptionBundleForExternalKey to fail after cancellation");
+        } catch (SubscriptionApiException e) {
+            assertEquals(e.getCode(), ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS.getCode());
+
+        }
+
         clock.addDays(1);
         // Re-create a new bundle with same externalKey
         final PlanPhaseSpecifier spec2 = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
index 9932e3f..3ebebb8 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
@@ -326,6 +326,77 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertNull(events.get(3).getNextPhase());
     }
 
+    @Test(groups = "fast")
+    public void testCancelBundleBeforeSubscription() throws CatalogApiException {
+        clock.setDay(new LocalDate(2013, 1, 1));
+
+        final DateTimeZone accountTimeZone = DateTimeZone.UTC;
+        final UUID accountId = UUID.randomUUID();
+        final UUID bundleId = UUID.randomUUID();
+        final String externalKey = "foo";
+
+        final UUID entitlementId = UUID.randomUUID();
+
+        final List<SubscriptionBaseTransition> allTransitions = new ArrayList<SubscriptionBaseTransition>();
+        final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
+
+        final DateTime requestedDate = new DateTime();
+        DateTime effectiveDate = new DateTime(2013, 1, 1, 15, 43, 25, 0, DateTimeZone.UTC);
+        final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
+        allTransitions.add(tr1);
+
+        // Block the bundle before the subscription
+        effectiveDate = effectiveDate.plusDays(15);
+        clock.addDays(15);
+        final BlockingState bs1 = new DefaultBlockingState(UUID.randomUUID(), bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                           DefaultEntitlementApi.ENT_STATE_CANCELLED, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                           true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+        blockingStates.add(bs1);
+
+        effectiveDate = effectiveDate.plusDays(15);
+        clock.addDays(15);
+        final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CANCEL, requestedDate, effectiveDate, clock.getUTCNow(), "trial", null);
+        allTransitions.add(tr2);
+
+        final List<Entitlement> entitlements = new ArrayList<Entitlement>();
+        final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
+        entitlements.add(entitlement);
+
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, blockingStates);
+
+        assertEquals(timeline.getAccountId(), accountId);
+        assertEquals(timeline.getBundleId(), bundleId);
+        assertEquals(timeline.getExternalKey(), externalKey);
+
+        final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
+        assertEquals(events.size(), 4);
+
+        assertEquals(events.get(0).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(1).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(2).getEffectiveDate().compareTo(new LocalDate(bs1.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(3).getEffectiveDate().compareTo(new LocalDate(tr2.getEffectiveTransitionTime(), accountTimeZone)), 0);
+
+        assertEquals(events.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
+        assertEquals(events.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
+        assertEquals(events.get(2).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
+        assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
+
+        assertEquals(events.get(0).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(1).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+        assertEquals(events.get(2).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(3).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+
+        assertNull(events.get(0).getPrevPhase());
+        assertEquals(events.get(0).getNextPhase().getName(), "trial");
+        assertNull(events.get(1).getPrevPhase());
+        assertEquals(events.get(1).getNextPhase().getName(), "trial");
+        assertEquals(events.get(2).getPrevPhase().getName(), "trial");
+        assertNull(events.get(2).getNextPhase());
+        assertEquals(events.get(3).getPrevPhase().getName(), "trial");
+        assertNull(events.get(3).getNextPhase());
+    }
+
     @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/135")
     public void testOneEntitlementWithPauseResume() throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
@@ -368,7 +439,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
         effectiveDate = effectiveDate.plusDays(15);
         clock.addDays(15);
-        final String service = "boo";
+        final String service = "boo-service-which-will-pause-billing";
         final BlockingState bs3 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
                                                            "NothingUseful3", service,
                                                            false, false, true, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
@@ -416,8 +487,8 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.RESUME_ENTITLEMENT);
         assertEquals(events.get(6).getSubscriptionEventType(), SubscriptionEventType.RESUME_BILLING);
 
-        assertEquals(events.get(7).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
-        assertEquals(events.get(8).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
+        assertEquals(events.get(7).getSubscriptionEventType(), SubscriptionEventType.PAUSE_BILLING);
+        assertEquals(events.get(8).getSubscriptionEventType(), SubscriptionEventType.RESUME_BILLING);
 
         assertEquals(events.get(0).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
         assertEquals(events.get(1).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
@@ -1081,6 +1152,143 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertNull(events.get(3).getNextPhase());
     }
 
+    @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/149")
+    public void testVariousBlockingStatesAtTheSameEffectiveDate() throws CatalogApiException {
+        clock.setDay(new LocalDate(2013, 1, 1));
+
+        final DateTimeZone accountTimeZone = DateTimeZone.UTC;
+        final UUID accountId = UUID.randomUUID();
+        final UUID bundleId = UUID.randomUUID();
+        final String externalKey = "foo";
+
+        final UUID entitlementId = UUID.randomUUID();
+
+        final List<SubscriptionBaseTransition> allTransitions = new ArrayList<SubscriptionBaseTransition>();
+        final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
+
+        final DateTime requestedDate = new DateTime();
+        DateTime effectiveDate = new DateTime(2013, 1, 1, 15, 43, 25, 0, DateTimeZone.UTC);
+        final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
+        allTransitions.add(tr1);
+
+        // 2013-02-10
+        effectiveDate = effectiveDate.plusDays(40);
+        clock.addDays(40);
+        final BlockingState bs1 = new DefaultBlockingState(UUID.randomUUID(), bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                           DefaultEntitlementApi.ENT_STATE_BLOCKED, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                           true, true, true, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+        blockingStates.add(bs1);
+        // Same timestamp on purpose
+        final BlockingState bs2 = new DefaultBlockingState(UUID.randomUUID(), bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                           DefaultEntitlementApi.ENT_STATE_CLEAR, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                           false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+        blockingStates.add(bs2);
+
+        // 2013-02-20
+        effectiveDate = effectiveDate.plusDays(10);
+        clock.addDays(10);
+        final BlockingState bs3 = new DefaultBlockingState(UUID.randomUUID(), bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                           DefaultEntitlementApi.ENT_STATE_BLOCKED, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                           true, true, true, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+        blockingStates.add(bs3);
+
+        // 2013-03-02
+        effectiveDate = effectiveDate.plusDays(10);
+        clock.addDays(10);
+        final BlockingState bs4 = new DefaultBlockingState(UUID.randomUUID(), bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                           DefaultEntitlementApi.ENT_STATE_CLEAR, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                           false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+        blockingStates.add(bs4);
+
+        final String overdueService = "overdue-service";
+        // 2013-03-04
+        effectiveDate = effectiveDate.plusDays(2);
+        clock.addDays(2);
+        final BlockingState bs5 = new DefaultBlockingState(UUID.randomUUID(), accountId, BlockingStateType.ACCOUNT,
+                                                           "OD1", overdueService,
+                                                           false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+        blockingStates.add(bs5);
+
+        final List<Entitlement> entitlements = new ArrayList<Entitlement>();
+        final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
+        entitlements.add(entitlement);
+
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, blockingStates);
+
+        final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
+        assertEquals(events.size(), 11);
+
+        assertEquals(events.get(0).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(1).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
+
+        assertEquals(events.get(2).getEffectiveDate().compareTo(new LocalDate(bs1.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(3).getEffectiveDate().compareTo(new LocalDate(bs1.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(4).getEffectiveDate().compareTo(new LocalDate(bs2.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(5).getEffectiveDate().compareTo(new LocalDate(bs2.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(6).getEffectiveDate().compareTo(new LocalDate(bs3.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(7).getEffectiveDate().compareTo(new LocalDate(bs3.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(8).getEffectiveDate().compareTo(new LocalDate(bs4.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(9).getEffectiveDate().compareTo(new LocalDate(bs4.getEffectiveDate(), accountTimeZone)), 0);
+
+        assertEquals(events.get(10).getEffectiveDate().compareTo(new LocalDate(bs5.getEffectiveDate(), accountTimeZone)), 0);
+
+        assertEquals(events.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
+        assertEquals(events.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
+
+        assertEquals(events.get(2).getSubscriptionEventType(), SubscriptionEventType.PAUSE_ENTITLEMENT);
+        assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.PAUSE_BILLING);
+        assertEquals(events.get(4).getSubscriptionEventType(), SubscriptionEventType.RESUME_ENTITLEMENT);
+        assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.RESUME_BILLING);
+
+        assertEquals(events.get(6).getSubscriptionEventType(), SubscriptionEventType.PAUSE_ENTITLEMENT);
+        assertEquals(events.get(7).getSubscriptionEventType(), SubscriptionEventType.PAUSE_BILLING);
+        assertEquals(events.get(8).getSubscriptionEventType(), SubscriptionEventType.RESUME_ENTITLEMENT);
+        assertEquals(events.get(9).getSubscriptionEventType(), SubscriptionEventType.RESUME_BILLING);
+
+        assertEquals(events.get(10).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
+
+        assertEquals(events.get(0).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(1).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+
+        assertEquals(events.get(2).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(3).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+        assertEquals(events.get(4).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(5).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+
+        assertEquals(events.get(6).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(7).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+        assertEquals(events.get(8).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(9).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+
+        assertEquals(events.get(10).getServiceName(), overdueService);
+
+        assertNull(events.get(0).getPrevPhase());
+        assertEquals(events.get(0).getNextPhase().getName(), "trial");
+        assertNull(events.get(1).getPrevPhase());
+        assertEquals(events.get(1).getNextPhase().getName(), "trial");
+
+        assertEquals(events.get(2).getPrevPhase().getName(), "trial");
+        assertEquals(events.get(2).getNextPhase().getName(), "trial");
+        assertEquals(events.get(3).getPrevPhase().getName(), "trial");
+        assertEquals(events.get(3).getNextPhase().getName(), "trial");
+        assertEquals(events.get(4).getPrevPhase().getName(), "trial");
+        assertEquals(events.get(4).getNextPhase().getName(), "trial");
+        assertEquals(events.get(5).getPrevPhase().getName(), "trial");
+        assertEquals(events.get(5).getNextPhase().getName(), "trial");
+
+        assertEquals(events.get(6).getPrevPhase().getName(), "trial");
+        assertEquals(events.get(6).getNextPhase().getName(), "trial");
+        assertEquals(events.get(7).getPrevPhase().getName(), "trial");
+        assertEquals(events.get(7).getNextPhase().getName(), "trial");
+        assertEquals(events.get(8).getPrevPhase().getName(), "trial");
+        assertEquals(events.get(8).getNextPhase().getName(), "trial");
+        assertEquals(events.get(9).getPrevPhase().getName(), "trial");
+        assertEquals(events.get(9).getNextPhase().getName(), "trial");
+
+        assertEquals(events.get(10).getPrevPhase().getName(), "trial");
+        assertEquals(events.get(10).getNextPhase().getName(), "trial");
+    }
+
     private Entitlement createEntitlement(final UUID entitlementId, final List<SubscriptionBaseTransition> allTransitions) {
         final DefaultEntitlement result = Mockito.mock(DefaultEntitlement.class);
         Mockito.when(result.getId()).thenReturn(entitlementId);
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
index 764872a..91dcf0e 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
@@ -40,6 +40,7 @@ import org.joda.time.LocalDate;
 import com.ning.billing.ObjectType;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.catalog.api.BillingActionPolicy;
 import com.ning.billing.clock.Clock;
 import com.ning.billing.entitlement.api.EntitlementApi;
 import com.ning.billing.entitlement.api.EntitlementApiException;
@@ -197,21 +198,22 @@ public class BundleResource extends JaxRsResourceBase {
     public Response transferBundle(final BundleJson json,
                                    @PathParam(ID_PARAM_NAME) final String id,
                                    @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
-                                   @QueryParam(QUERY_BUNDLE_TRANSFER_ADDON) @DefaultValue("true") final Boolean transferAddOn,
-                                   @QueryParam(QUERY_BUNDLE_TRANSFER_CANCEL_IMM) @DefaultValue("false") final Boolean cancelImmediatley,
+                                   @QueryParam(QUERY_BILLING_POLICY) @DefaultValue("END_OF_TERM") final String policyString,
                                    @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                    @HeaderParam(HDR_REASON) final String reason,
                                    @HeaderParam(HDR_COMMENT) final String comment,
                                    @javax.ws.rs.core.Context final UriInfo uriInfo,
                                    @javax.ws.rs.core.Context final HttpServletRequest request) throws EntitlementApiException, SubscriptionApiException, AccountApiException {
 
+        final BillingActionPolicy policy = BillingActionPolicy.valueOf(policyString.toUpperCase());
+
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
         final UUID bundleId = UUID.fromString(id);
 
         final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, callContext);
         final LocalDate inputLocalDate = toLocalDate(bundle.getAccountId(), requestedDate, callContext);
 
-        final UUID newBundleId = entitlementApi.transferEntitlements(bundle.getAccountId(), UUID.fromString(json.getAccountId()), bundle.getExternalKey(), inputLocalDate, callContext);
+        final UUID newBundleId = entitlementApi.transferEntitlementsOverrideBillingPolicy(bundle.getAccountId(), UUID.fromString(json.getAccountId()), bundle.getExternalKey(), inputLocalDate, policy, callContext);
         return uriBuilder.buildResponse(BundleResource.class, "getBundle", newBundleId, uriInfo.getBaseUri().toString());
     }
 

NEWS 11(+11 -0)

diff --git a/NEWS b/NEWS
index 3a050f2..06a5585 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,14 @@
+0.8.7
+    DDL: remove unused paid_through_date column
+
+0.8.6
+    Partial fix for https://github.com/killbill/killbill/issues/141
+    https://github.com/killbill/killbill/issues/143
+    https://github.com/killbill/killbill/issues/145
+    https://github.com/killbill/killbill/issues/147
+    https://github.com/killbill/killbill/issues/148
+    DDL: remove unused billing_cycle_day_utc column
+
 0.8.5
     https://github.com/killbill/killbill/issues/134
     https://github.com/killbill/killbill/issues/135
diff --git a/subscription/src/main/java/com/ning/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/com/ning/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index bde3dfd..25336b3 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -163,10 +163,6 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
     public SubscriptionBaseBundle createBundleForAccount(final UUID accountId, final String bundleKey, final InternalCallContext context) throws SubscriptionBaseApiException {
 
         final List<SubscriptionBaseBundle> existingBundles = dao.getSubscriptionBundlesForKey(bundleKey, context);
-        final SubscriptionBaseBundle result = getActiveBundleForKeyNotException(existingBundles, dao, clock, context);
-        if (result != null) {
-            throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS, bundleKey);
-        }
         final DateTime now = clock.getUTCNow();
         final DateTime originalCreatedDate = existingBundles.size() > 0 ? existingBundles.get(0).getCreatedDate() : now;
         final DefaultSubscriptionBaseBundle bundle = new DefaultSubscriptionBaseBundle(bundleKey, accountId, now, originalCreatedDate, now, now);
@@ -191,14 +187,8 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
     }
 
     @Override
-    public SubscriptionBaseBundle getActiveBundleForKey(final String bundleKey, final InternalTenantContext context) throws SubscriptionBaseApiException  {
-        final List<SubscriptionBaseBundle> existingBundles = dao.getSubscriptionBundlesForKey(bundleKey, context);
-
-        final SubscriptionBaseBundle result =  getActiveBundleForKeyNotException(existingBundles, dao, clock, context);
-        if (result == null) {
-            throw new SubscriptionBaseApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_KEY, bundleKey);
-        }
-        return result;
+    public Iterable<UUID> getNonAOSubscriptionIdsForKey(final String bundleKey, final InternalTenantContext context) {
+        return dao.getNonAOSubscriptionIdsForKey(bundleKey, context);
     }
 
     public static SubscriptionBaseBundle getActiveBundleForKeyNotException(final List<SubscriptionBaseBundle> existingBundles, final SubscriptionDao dao, final Clock clock, final InternalTenantContext context)  {
@@ -273,8 +263,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                                       DateTime chargedThruDate, InternalCallContext context) {
         final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) dao.getSubscriptionFromId(subscriptionId, context);
         final SubscriptionBuilder builder = new SubscriptionBuilder(subscription)
-                .setChargedThroughDate(chargedThruDate)
-                .setPaidThroughDate(subscription.getPaidThroughDate());
+                .setChargedThroughDate(chargedThruDate);
 
         dao.updateChargedThroughDate(new DefaultSubscriptionBase(builder), context);
     }
diff --git a/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBase.java b/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBase.java
index 4faf2f2..bb191d5 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBase.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/api/user/DefaultSubscriptionBase.java
@@ -78,7 +78,6 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
     //
     private final long activeVersion;
     private final DateTime chargedThroughDate;
-    private final DateTime paidThroughDate;
 
     //
     // User APIs (create, change, cancelWithRequestedDate,...) will recompute those each time,
@@ -110,7 +109,6 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         this.category = builder.getCategory();
         this.activeVersion = builder.getActiveVersion();
         this.chargedThroughDate = builder.getChargedThroughDate();
-        this.paidThroughDate = builder.getPaidThroughDate();
     }
 
     // Used for API to make sure we have a clock and an apiService set before we return the object
@@ -124,7 +122,6 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         this.category = internalSubscription.getCategory();
         this.activeVersion = internalSubscription.getActiveVersion();
         this.chargedThroughDate = internalSubscription.getChargedThroughDate();
-        this.paidThroughDate = internalSubscription.getPaidThroughDate();
         this.transitions = new LinkedList<SubscriptionBaseTransition>(internalSubscription.getAllTransitions());
         this.events = internalSubscription.getEvents();
     }
@@ -349,11 +346,6 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
     }
 
     @Override
-    public DateTime getPaidThroughDate() {
-        return paidThroughDate;
-    }
-
-    @Override
     public List<SubscriptionBaseTransition> getAllTransitions() {
         if (transitions == null) {
             return Collections.emptyList();
diff --git a/subscription/src/main/java/com/ning/billing/subscription/api/user/SubscriptionBuilder.java b/subscription/src/main/java/com/ning/billing/subscription/api/user/SubscriptionBuilder.java
index 94f23f7..2347887 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/api/user/SubscriptionBuilder.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/api/user/SubscriptionBuilder.java
@@ -25,6 +25,7 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.subscription.exceptions.SubscriptionBaseError;
 
 public class SubscriptionBuilder {
+
     private UUID id;
     private UUID bundleId;
     private DateTime createdDate;
@@ -34,7 +35,6 @@ public class SubscriptionBuilder {
     private Long activeVersion;
     private ProductCategory category;
     private DateTime chargedThroughDate;
-    private DateTime paidThroughDate;
 
     public SubscriptionBuilder() {
         this.activeVersion = SubscriptionEvents.INITIAL_VERSION;
@@ -48,7 +48,6 @@ public class SubscriptionBuilder {
         this.category = original.getCategory();
         this.activeVersion = original.getActiveVersion();
         this.chargedThroughDate = original.getChargedThroughDate();
-        this.paidThroughDate = original.getPaidThroughDate();
     }
 
     public SubscriptionBuilder setId(final UUID id) {
@@ -91,11 +90,6 @@ public class SubscriptionBuilder {
         return this;
     }
 
-    public SubscriptionBuilder setPaidThroughDate(final DateTime paidThroughDate) {
-        this.paidThroughDate = paidThroughDate;
-        return this;
-    }
-
     public SubscriptionBuilder setCategory(final ProductCategory category) {
         this.category = category;
         return this;
@@ -137,21 +131,17 @@ public class SubscriptionBuilder {
         return chargedThroughDate;
     }
 
-    public DateTime getPaidThroughDate() {
-        return paidThroughDate;
-    }
-
     private void checkAllFieldsSet() {
         for (final Field cur : SubscriptionBuilder.class.getDeclaredFields()) {
             try {
                 final Object value = cur.get(this);
                 if (value == null) {
                     throw new SubscriptionBaseError(String.format("Field %s has not been set for SubscriptionBase",
-                                                             cur.getName()));
+                                                                  cur.getName()));
                 }
             } catch (IllegalAccessException e) {
                 throw new SubscriptionBaseError(String.format("Failed to access value for field %s for SubscriptionBase",
-                                                         cur.getName()), e);
+                                                              cur.getName()), e);
             }
         }
     }
diff --git a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 21edeeb..07096eb 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -23,6 +23,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -93,8 +94,9 @@ import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Collections2;
-import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 
 public class DefaultSubscriptionDao implements SubscriptionDao {
@@ -181,6 +183,43 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
     }
 
     @Override
+    public Iterable<UUID> getNonAOSubscriptionIdsForKey(final String bundleKey, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Iterable<UUID>>() {
+            @Override
+            public Iterable<UUID> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+
+                final BundleSqlDao bundleSqlDao = entitySqlDaoWrapperFactory.become(BundleSqlDao.class);
+
+                final List<SubscriptionBundleModelDao> bundles = bundleSqlDao.getBundlesForKey(bundleKey, context);
+
+                final SubscriptionSqlDao subscriptionSqlDao = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class);
+                return Iterables.concat(Iterables.transform(bundles, new Function<SubscriptionBundleModelDao, Iterable<UUID>>() {
+
+                    @Override
+                    public Iterable<UUID> apply(final SubscriptionBundleModelDao cur) {
+
+                        final List<SubscriptionModelDao> subscriptions = subscriptionSqlDao.getSubscriptionsFromBundleId(cur.getId().toString(), context);
+
+                        final Iterable<SubscriptionModelDao> nonAddonSubscriptions = Iterables.filter(subscriptions, new Predicate<SubscriptionModelDao>() {
+                            @Override
+                            public boolean apply(final SubscriptionModelDao input) {
+                                return input.getCategory() != ProductCategory.ADD_ON;
+                            }
+                        });
+
+                        return Iterables.transform(nonAddonSubscriptions, new Function<SubscriptionModelDao, UUID>() {
+                            @Override
+                            public UUID apply(final SubscriptionModelDao input) {
+                                return input.getId();
+                            }
+                        });
+                    }
+                }));
+            }
+        });
+    }
+
+    @Override
     public SubscriptionBaseBundle createSubscriptionBundle(final DefaultSubscriptionBaseBundle bundle, final InternalCallContext context) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<SubscriptionBaseBundle>() {
             @Override
@@ -259,7 +298,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         });
     }
 
-
     @Override
     public Map<UUID, List<SubscriptionBase>> getSubscriptionsForAccount(final InternalTenantContext context) {
         final Map<UUID, List<SubscriptionBase>> subscriptionsFromAccountId = getSubscriptionsFromAccountId(context);
@@ -279,7 +317,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
             final Multimap<UUID, SubscriptionBaseEvent> eventsForSubscriptions = ArrayListMultimap.create();
 
             for (final SubscriptionBase cur : subscriptionsForBundle) {
-                final Collection<SubscriptionBaseEvent> events= Collections2.filter(eventsForAccount, new Predicate<SubscriptionBaseEvent>() {
+                final Collection<SubscriptionBaseEvent> events = Collections2.filter(eventsForAccount, new Predicate<SubscriptionBaseEvent>() {
                     @Override
                     public boolean apply(final SubscriptionBaseEvent input) {
                         return input.getSubscriptionId().equals(cur.getId());
@@ -289,7 +327,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
                 eventsForSubscriptions.putAll(cur.getId(), ImmutableList.copyOf(events));
             }
 
-            result.put(bundleId, buildBundleSubscriptions(subscriptionsForBundle, eventsForSubscriptions,context));
+            result.put(bundleId, buildBundleSubscriptions(subscriptionsForBundle, eventsForSubscriptions, context));
         }
         return result;
     }
@@ -379,7 +417,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         });
     }
 
-
     @Override
     public Map<UUID, List<SubscriptionBaseEvent>> getEventsForBundle(final UUID bundleId, final InternalTenantContext context) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Map<UUID, List<SubscriptionBaseEvent>>>() {
@@ -800,7 +837,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         final List<SubscriptionBase> result = new ArrayList<SubscriptionBase>(input.size());
         for (final SubscriptionBase cur : input) {
 
-
             final List<SubscriptionBaseEvent> events = eventsForSubscription != null ?
                                                        (List<SubscriptionBaseEvent>) eventsForSubscription.get(cur.getId()) :
                                                        getEventsForSubscription(cur.getId(), context);
diff --git a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionModelDao.java b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionModelDao.java
index bd181c6..9defa26 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionModelDao.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionModelDao.java
@@ -36,12 +36,11 @@ public class SubscriptionModelDao extends EntityBase implements EntityModelDao<S
     private DateTime bundleStartDate;
     private long activeVersion;
     private DateTime chargedThroughDate;
-    private DateTime paidThroughDate;
 
     public SubscriptionModelDao() { /* For the DAO mapper */ }
 
     public SubscriptionModelDao(final UUID id, final UUID bundleId, final ProductCategory category, final DateTime startDate, final DateTime bundleStartDate,
-                                final long activeVersion, final DateTime chargedThroughDate, final DateTime paidThroughDate, final DateTime createdDate, final DateTime updateDate) {
+                                final long activeVersion, final DateTime chargedThroughDate, final DateTime createdDate, final DateTime updateDate) {
         super(id, createdDate, updateDate);
         this.bundleId = bundleId;
         this.category = category;
@@ -49,12 +48,11 @@ public class SubscriptionModelDao extends EntityBase implements EntityModelDao<S
         this.bundleStartDate = bundleStartDate;
         this.activeVersion = activeVersion;
         this.chargedThroughDate = chargedThroughDate;
-        this.paidThroughDate = paidThroughDate;
     }
 
     public SubscriptionModelDao(final DefaultSubscriptionBase src) {
         this(src.getId(), src.getBundleId(), src.getCategory(), src.getAlignStartDate(), src.getBundleStartDate(), src.getActiveVersion(),
-             src.getChargedThroughDate(), src.getPaidThroughDate(), src.getCreatedDate(), src.getUpdatedDate());
+             src.getChargedThroughDate(), src.getCreatedDate(), src.getUpdatedDate());
     }
 
     public UUID getBundleId() {
@@ -81,10 +79,6 @@ public class SubscriptionModelDao extends EntityBase implements EntityModelDao<S
         return chargedThroughDate;
     }
 
-    public DateTime getPaidThroughDate() {
-        return paidThroughDate;
-    }
-
     public void setBundleId(final UUID bundleId) {
         this.bundleId = bundleId;
     }
@@ -109,10 +103,6 @@ public class SubscriptionModelDao extends EntityBase implements EntityModelDao<S
         this.chargedThroughDate = chargedThroughDate;
     }
 
-    public void setPaidThroughDate(final DateTime paidThroughDate) {
-        this.paidThroughDate = paidThroughDate;
-    }
-
     public static SubscriptionBase toSubscription(final SubscriptionModelDao src) {
         if (src == null) {
             return null;
@@ -127,7 +117,6 @@ public class SubscriptionModelDao extends EntityBase implements EntityModelDao<S
                                             .setAlignStartDate(src.getStartDate())
                                             .setActiveVersion(src.getActiveVersion())
                                             .setChargedThroughDate(src.getChargedThroughDate())
-                                            .setPaidThroughDate(src.getPaidThroughDate())
                                             .setCreatedDate(src.getCreatedDate())
                                             .setUpdatedDate(src.getUpdatedDate()));
     }
@@ -142,7 +131,6 @@ public class SubscriptionModelDao extends EntityBase implements EntityModelDao<S
         sb.append(", bundleStartDate=").append(bundleStartDate);
         sb.append(", activeVersion=").append(activeVersion);
         sb.append(", chargedThroughDate=").append(chargedThroughDate);
-        sb.append(", paidThroughDate=").append(paidThroughDate);
         sb.append('}');
         return sb.toString();
     }
@@ -176,9 +164,6 @@ public class SubscriptionModelDao extends EntityBase implements EntityModelDao<S
         if (chargedThroughDate != null ? !chargedThroughDate.equals(that.chargedThroughDate) : that.chargedThroughDate != null) {
             return false;
         }
-        if (paidThroughDate != null ? !paidThroughDate.equals(that.paidThroughDate) : that.paidThroughDate != null) {
-            return false;
-        }
         if (startDate != null ? !startDate.equals(that.startDate) : that.startDate != null) {
             return false;
         }
@@ -195,7 +180,6 @@ public class SubscriptionModelDao extends EntityBase implements EntityModelDao<S
         result = 31 * result + (bundleStartDate != null ? bundleStartDate.hashCode() : 0);
         result = 31 * result + (int) (activeVersion ^ (activeVersion >>> 32));
         result = 31 * result + (chargedThroughDate != null ? chargedThroughDate.hashCode() : 0);
-        result = 31 * result + (paidThroughDate != null ? paidThroughDate.hashCode() : 0);
         return result;
     }
 
diff --git a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/RepairSubscriptionDao.java b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/RepairSubscriptionDao.java
index b05e650..fe12bc3 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/RepairSubscriptionDao.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/RepairSubscriptionDao.java
@@ -306,6 +306,11 @@ public class RepairSubscriptionDao implements SubscriptionDao, RepairSubscriptio
     }
 
     @Override
+    public List<UUID> getNonAOSubscriptionIdsForKey(final String bundleKey, final InternalTenantContext context) {
+        throw new SubscriptionBaseError(NOT_IMPLEMENTED);
+    }
+
+    @Override
     public List<SubscriptionBaseBundle> getSubscriptionBundlesForAccountAndKey(final UUID accountId, final String bundleKey, final InternalTenantContext context) {
         throw new SubscriptionBaseError(NOT_IMPLEMENTED);
     }
diff --git a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/SubscriptionDao.java b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/SubscriptionDao.java
index b7cffc0..77229e4 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/SubscriptionDao.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/SubscriptionDao.java
@@ -39,6 +39,8 @@ public interface SubscriptionDao {
 
     public List<SubscriptionBaseBundle> getSubscriptionBundlesForKey(String bundleKey, InternalTenantContext context);
 
+    public Iterable<UUID> getNonAOSubscriptionIdsForKey(final String bundleKey, final InternalTenantContext context);
+
     public List<SubscriptionBaseBundle> getSubscriptionBundlesForAccountAndKey(UUID accountId, String bundleKey, InternalTenantContext context);
 
     public SubscriptionBaseBundle getSubscriptionBundleFromId(UUID bundleId, InternalTenantContext context);
diff --git a/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg b/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg
index ad122a2..55b2cdb 100644
--- a/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg
+++ b/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg
@@ -9,7 +9,6 @@ tableFields(prefix) ::= <<
 , <prefix>bundle_start_date
 , <prefix>active_version
 , <prefix>charged_through_date
-, <prefix>paid_through_date
 , <prefix>created_by
 , <prefix>created_date
 , <prefix>updated_by
@@ -23,7 +22,6 @@ tableValues() ::= <<
 , :bundleStartDate
 , :activeVersion
 , :chargedThroughDate
-, :paidThroughDate
 , :createdBy
 , :createdDate
 , :updatedBy
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java b/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java
index 62cf563..8b5af59 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java
@@ -99,10 +99,6 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
         // The MIGRATE_BILLING event should have been invalidated
         assertEquals(subscriptionInternalApi.getBillingTransitions(oldBaseSubscription, internalCallContext).size(), 0);
         //assertEquals(subscriptionInternalApi.getBillingTransitions(oldBaseSubscription, internalCallContext).get(0).getTransitionType(), SubscriptionBaseTransitionType.CANCEL);
-
-        final SubscriptionBaseBundle newBundle = subscriptionInternalApi.getActiveBundleForKey(bundle.getExternalKey(), internalCallContext);
-        assertNotEquals(newBundle.getId(), bundle.getId());
-        assertEquals(newBundle.getOriginalCreatedDate().compareTo(bundleCreatedDate), 0);
     }
 
     @Test(groups = "slow")
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCreate.java b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCreate.java
index de6eef8..ebb176c 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCreate.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/user/TestUserApiCreate.java
@@ -57,14 +57,6 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
         assertListenerStatus();
         assertNotNull(subscription);
 
-        try {
-            final SubscriptionBaseBundle newBundle = subscriptionInternalApi.createBundleForAccount(bundle.getAccountId(), DefaultSubscriptionTestInitializer.DEFAULT_BUNDLE_KEY, internalCallContext);
-
-            Assert.fail("Unexpected success to create a bundle");
-        } catch (SubscriptionBaseApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS.getCode());
-        }
-
         testListener.pushExpectedEvent(NextEvent.CANCEL);
         subscription.cancelWithDate(clock.getUTCNow(), callContext);
         assertListenerStatus();
diff --git a/subscription/src/test/java/com/ning/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java b/subscription/src/test/java/com/ning/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
index 77ce836..4d23fd3 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
@@ -114,6 +114,11 @@ public class MockSubscriptionDaoMemory implements SubscriptionDao {
     }
 
     @Override
+    public List<UUID> getNonAOSubscriptionIdsForKey(final String bundleKey, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public SubscriptionBaseBundle getSubscriptionBundleFromId(final UUID bundleId, final InternalTenantContext context) {
         for (final SubscriptionBaseBundle cur : bundles) {
             if (cur.getId().equals(bundleId)) {
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 a663791..f32ce7a 100644
--- a/util/src/test/java/com/ning/billing/mock/MockSubscription.java
+++ b/util/src/test/java/com/ning/billing/mock/MockSubscription.java
@@ -166,11 +166,6 @@ public class MockSubscription implements SubscriptionBase {
     }
 
     @Override
-    public DateTime getPaidThroughDate() {
-        return sub.getPaidThroughDate();
-    }
-
-    @Override
     public ProductCategory getCategory() {
         return sub.getCategory();
     }