killbill-memoizeit

Changes

pom.xml 2(+1 -1)

Details

diff --git a/api/src/main/java/com/ning/billing/entitlement/EventsStream.java b/api/src/main/java/com/ning/billing/entitlement/EventsStream.java
index b570fd3..8cb930f 100644
--- a/api/src/main/java/com/ning/billing/entitlement/EventsStream.java
+++ b/api/src/main/java/com/ning/billing/entitlement/EventsStream.java
@@ -60,6 +60,8 @@ public interface EventsStream {
 
     Collection<BlockingState> getPendingEntitlementCancellationEvents();
 
+    BlockingState getEntitlementCancellationEvent();
+
     // All blocking states for the account, associated bundle or subscription
     Collection<BlockingState> getBlockingStates();
 
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
index ac479f7..5576a98 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
@@ -289,15 +289,28 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         // Get the latest state from disk
         refresh(callContext);
 
-        if (eventsStream.isEntitlementCancelled() || eventsStream.isSubscriptionCancelled()) {
+        if (eventsStream.isSubscriptionCancelled()) {
             throw new EntitlementApiException(ErrorCode.SUB_CANCEL_BAD_STATE, getId(), EntitlementState.CANCELLED);
         }
 
-        // Reactivate entitlements
-        // See also https://github.com/killbill/killbill/issues/111
         final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
-        for (final BlockingState futureCancellation : eventsStream.getPendingEntitlementCancellationEvents()) {
-            blockingStateDao.unactiveBlockingState(futureCancellation.getId(), contextWithValidAccountRecordId);
+        final Collection<BlockingState> pendingEntitlementCancellationEvents = eventsStream.getPendingEntitlementCancellationEvents();
+        if (eventsStream.isEntitlementCancelled()) {
+            final BlockingState cancellationEvent = eventsStream.getEntitlementCancellationEvent();
+            blockingStateDao.unactiveBlockingState(cancellationEvent.getId(), contextWithValidAccountRecordId);
+        } else if (pendingEntitlementCancellationEvents.size() > 0) {
+            // Reactivate entitlements
+            // See also https://github.com/killbill/killbill/issues/111
+            //
+            // Today we only support cancellation at SUBSCRIPTION level (Not ACCOUNT or BUNDLE), so we should really have only
+            // one future event in the list
+            //
+            for (final BlockingState futureCancellation : pendingEntitlementCancellationEvents) {
+                blockingStateDao.unactiveBlockingState(futureCancellation.getId(), contextWithValidAccountRecordId);
+            }
+        } else {
+            // Entitlement is NOT cancelled (or future cancelled), there is nothing to do
+            throw new EntitlementApiException(ErrorCode.SUB_CANCEL_BAD_STATE, getId(), EntitlementState.CANCELLED);
         }
 
         // If billing was previously cancelled, reactivate
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateDao.java
index aaa286b..3dd9cdd 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateDao.java
@@ -51,27 +51,6 @@ public interface BlockingStateDao extends EntityDao<BlockingStateModelDao, Block
     public List<BlockingState> getBlockingState(UUID blockableId, BlockingStateType blockingStateType, InternalTenantContext context);
 
     /**
-     * Returns the state history for that specific service
-     *
-     * @param blockableId       id of the blockable object
-     * @param blockingStateType blockable object type
-     * @param serviceName       name of the service
-     * @param context           call context
-     * @return list of blocking states for that blockable object and service
-     */
-    public List<BlockingState> getBlockingHistoryForService(UUID blockableId, BlockingStateType blockingStateType, String serviceName, InternalTenantContext context);
-
-    /**
-     * Return all the events (past and future) across all services
-     *
-     * @param blockableId       id of the blockable object
-     * @param blockingStateType blockable object type
-     * @param context           call context
-     * @return list of blocking states for that blockable object
-     */
-    public List<BlockingState> getBlockingAll(UUID blockableId, BlockingStateType blockingStateType, InternalTenantContext context);
-
-    /**
      * Return all events (past and future) across all services) for a given callcontext (account_record_id)
      *
      * @param context call context
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateSqlDao.java
index e95ebe9..c69758b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateSqlDao.java
@@ -57,23 +57,16 @@ public interface BlockingStateSqlDao extends EntitySqlDao<BlockingStateModelDao,
                                                                  @Bind("effectiveDate") Date effectiveDate,
                                                                  @BindBean final InternalTenantContext context);
 
-
     @SqlQuery
     public abstract List<BlockingStateModelDao> getBlockingHistoryForService(@Bind("blockableId") UUID blockableId,
                                                                              @Bind("service") String serviceName,
                                                                              @BindBean final InternalTenantContext context);
 
-    @SqlQuery
-    public abstract List<BlockingStateModelDao> getBlockingAll(@Bind("blockableId") UUID blockableId,
-                                                               @BindBean final InternalTenantContext context);
-
-
     @SqlUpdate
     @Audited(ChangeType.UPDATE)
     public void unactiveEvent(@Bind("id") String id,
                               @BindBean final InternalCallContext context);
 
-
     public class BlockingHistorySqlMapper extends MapperBase implements ResultSetMapper<BlockingStateModelDao> {
 
         @Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/DefaultBlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/DefaultBlockingStateDao.java
index 1292cbf..7f21843 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/DefaultBlockingStateDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/DefaultBlockingStateDao.java
@@ -111,44 +111,6 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
     }
 
     @Override
-    public List<BlockingState> getBlockingHistoryForService(final UUID blockableId, final BlockingStateType blockingStateType, final String serviceName, final InternalTenantContext context) {
-        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<BlockingState>>() {
-            @Override
-            public List<BlockingState> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                final BlockingStateSqlDao sqlDao = entitySqlDaoWrapperFactory.become(BlockingStateSqlDao.class);
-                final List<BlockingStateModelDao> models = sqlDao.getBlockingHistoryForService(blockableId, serviceName, context);
-                final Collection<BlockingStateModelDao> modelsFiltered = filterBlockingStates(models, blockingStateType);
-                return new ArrayList<BlockingState>(Collections2.transform(modelsFiltered,
-                                                                           new Function<BlockingStateModelDao, BlockingState>() {
-                                                                               @Override
-                                                                               public BlockingState apply(@Nullable final BlockingStateModelDao src) {
-                                                                                   return BlockingStateModelDao.toBlockingState(src);
-                                                                               }
-                                                                           }));
-            }
-        });
-    }
-
-    @Override
-    public List<BlockingState> getBlockingAll(final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
-        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<BlockingState>>() {
-            @Override
-            public List<BlockingState> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                final BlockingStateSqlDao sqlDao = entitySqlDaoWrapperFactory.become(BlockingStateSqlDao.class);
-                final List<BlockingStateModelDao> models = sqlDao.getBlockingAll(blockableId, context);
-                final Collection<BlockingStateModelDao> modelsFiltered = filterBlockingStates(models, blockingStateType);
-                return new ArrayList<BlockingState>(Collections2.transform(modelsFiltered,
-                                                                           new Function<BlockingStateModelDao, BlockingState>() {
-                                                                               @Override
-                                                                               public BlockingState apply(@Nullable final BlockingStateModelDao src) {
-                                                                                   return BlockingStateModelDao.toBlockingState(src);
-                                                                               }
-                                                                           }));
-            }
-        });
-    }
-
-    @Override
     public List<BlockingState> getBlockingAllForAccountRecordId(final InternalTenantContext context) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<BlockingState>>() {
             @Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java
index d810635..4f71fba 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java
@@ -23,6 +23,7 @@ import javax.annotation.Nullable;
 
 import org.skife.jdbi.v2.IDBI;
 
+import com.ning.billing.account.api.Account;
 import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.clock.Clock;
@@ -47,8 +48,31 @@ public class OptimizedProxyBlockingStateDao extends ProxyBlockingStateDao {
         super(eventsStreamBuilder, subscriptionBaseInternalApi, dbi, clock, cacheControllerDispatcher, nonEntityDao);
     }
 
-    // Special signature for EventsStreamBuilder to save some DAO calls
-    public List<BlockingState> getBlockingHistory(final List<BlockingState> blockingStatesOnDisk,
+    /**
+     * Retrieve blocking states for a given subscription
+     * <p/>
+     * If the specified subscription is not an add-on, we already have the blocking states
+     * (they are all on disk) - we simply return them and there is nothing to do.
+     * Otherwise, for add-ons, we will need to compute the blocking states not on disk.
+     * <p/>
+     * This is a special method for EventsStreamBuilder to save some DAO calls.
+     *
+     * @param subscriptionBlockingStatesOnDisk
+     *                                  blocking states on disk for that subscription
+     * @param allBlockingStatesOnDiskForAccount
+     *                                  all blocking states on disk for that account
+     * @param account                   account associated with the subscription
+     * @param bundle                    bundle associated with the subscription
+     * @param baseSubscription          base subscription (ProductCategory.BASE) associated with that bundle
+     * @param subscription              subscription for which to build blocking states
+     * @param allSubscriptionsForBundle all subscriptions associated with that bundle
+     * @param context                   call context
+     * @return blocking states for that subscription
+     * @throws EntitlementApiException
+     */
+    public List<BlockingState> getBlockingHistory(final List<BlockingState> subscriptionBlockingStatesOnDisk,
+                                                  final List<BlockingState> allBlockingStatesOnDiskForAccount,
+                                                  final Account account,
                                                   final SubscriptionBaseBundle bundle,
                                                   @Nullable final SubscriptionBase baseSubscription,
                                                   final SubscriptionBase subscription,
@@ -57,18 +81,20 @@ public class OptimizedProxyBlockingStateDao extends ProxyBlockingStateDao {
         // blockable id points to a subscription, but make sure it's an add-on
         if (!ProductCategory.ADD_ON.equals(subscription.getCategory())) {
             // blockable id points to a base or standalone subscription, there is nothing to do
-            return blockingStatesOnDisk;
+            return subscriptionBlockingStatesOnDisk;
         }
 
         // Find all base entitlements that we care about (for which we want to find future cancelled add-ons)
-        final Iterable<EventsStream> eventsStreams = ImmutableList.<EventsStream>of(eventsStreamBuilder.buildForEntitlement(bundle,
+        final Iterable<EventsStream> eventsStreams = ImmutableList.<EventsStream>of(eventsStreamBuilder.buildForEntitlement(allBlockingStatesOnDiskForAccount,
+                                                                                                                            account,
+                                                                                                                            bundle,
                                                                                                                             baseSubscription,
                                                                                                                             allSubscriptionsForBundle,
                                                                                                                             context));
 
         return addBlockingStatesNotOnDisk(subscription.getId(),
                                           BlockingStateType.SUBSCRIPTION,
-                                          new LinkedList<BlockingState>(blockingStatesOnDisk),
+                                          new LinkedList<BlockingState>(subscriptionBlockingStatesOnDisk),
                                           ImmutableList.<SubscriptionBase>of(baseSubscription),
                                           eventsStreams);
     }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
index 7662f22..bef2260 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
@@ -18,6 +18,7 @@ package com.ning.billing.entitlement.dao;
 
 import java.util.Collection;
 import java.util.Comparator;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -41,18 +42,16 @@ import com.ning.billing.entitlement.EventsStream;
 import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
 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.engine.core.EventsStreamBuilder;
 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.util.cache.CacheControllerDispatcher;
+import com.ning.billing.util.customfield.ShouldntHappenException;
 import com.ning.billing.util.dao.NonEntityDao;
 import com.ning.billing.util.entity.Pagination;
 
 import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Ordering;
 
@@ -62,7 +61,101 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
     private static final Logger log = LoggerFactory.getLogger(ProxyBlockingStateDao.class);
 
     // Ordering is critical here, especially for Junction
-    public static final Ordering<BlockingState> BLOCKING_STATE_ORDERING = Ordering.<BlockingState>from(new Comparator<BlockingState>() {
+    public static List<BlockingState> sortedCopy(final Iterable<BlockingState> blockingStates) {
+        final List<BlockingState> blockingStatesSomewhatSorted = BLOCKING_STATE_ORDERING_WITH_TIES_UNHANDLED.immutableSortedCopy(blockingStates);
+
+        final List<BlockingState> result = new LinkedList<BlockingState>();
+
+        // Take care of the ties
+        final Iterator<BlockingState> iterator = blockingStatesSomewhatSorted.iterator();
+        BlockingState prev = null;
+        while (iterator.hasNext()) {
+            final BlockingState current = iterator.next();
+            if (iterator.hasNext()) {
+                final BlockingState next = iterator.next();
+                if (prev != null && current.getEffectiveDate().equals(next.getEffectiveDate()) && current.getBlockedId().equals(next.getBlockedId())) {
+                    // Same date, same blockable id
+
+                    // Make sure block billing transitions are respected first
+                    BlockingState prevCandidate = insertTiedBlockingStatesInTheRightOrder(result, current, next, prev.isBlockBilling(), current.isBlockBilling(), next.isBlockBilling());
+                    if (prevCandidate == null) {
+                        // Then respect block entitlement transitions
+                        prevCandidate = insertTiedBlockingStatesInTheRightOrder(result, current, next, prev.isBlockEntitlement(), current.isBlockEntitlement(), next.isBlockEntitlement());
+                        if (prevCandidate == null) {
+                            // And finally block changes transitions
+                            prevCandidate = insertTiedBlockingStatesInTheRightOrder(result, current, next, prev.isBlockChange(), current.isBlockChange(), next.isBlockChange());
+                            if (prevCandidate == null) {
+                                // Trust the creation date (see BLOCKING_STATE_ORDERING_WITH_TIES_UNHANDLED below)
+                                result.add(current);
+                                result.add(next);
+                                prev = next;
+                            } else {
+                                prev = prevCandidate;
+                            }
+                        } else {
+                            prev = prevCandidate;
+                        }
+                    } else {
+                        prev = prevCandidate;
+                    }
+                } else {
+                    result.add(current);
+                    result.add(next);
+                    prev = next;
+                }
+            } else {
+                // End of the list
+                result.add(current);
+            }
+        }
+
+        return result;
+    }
+
+    private static BlockingState insertTiedBlockingStatesInTheRightOrder(final Collection<BlockingState> result,
+                                                                         final BlockingState current,
+                                                                         final BlockingState next,
+                                                                         final boolean prevBlocked,
+                                                                         final boolean currentBlocked,
+                                                                         final boolean nextBlocked) {
+        final BlockingState prev;
+
+        if (prevBlocked && currentBlocked && nextBlocked) {
+            // Tricky use case, bail
+            return null;
+        } else if (prevBlocked && currentBlocked && !nextBlocked) {
+            result.add(next);
+            result.add(current);
+            prev = current;
+        } else if (prevBlocked && !currentBlocked && nextBlocked) {
+            result.add(current);
+            result.add(next);
+            prev = next;
+        } else if (prevBlocked && !currentBlocked && !nextBlocked) {
+            // Tricky use case, bail
+            return null;
+        } else if (!prevBlocked && currentBlocked && nextBlocked) {
+            // Tricky use case, bail
+            return null;
+        } else if (!prevBlocked && currentBlocked && !nextBlocked) {
+            result.add(current);
+            result.add(next);
+            prev = next;
+        } else if (!prevBlocked && !currentBlocked && nextBlocked) {
+            result.add(next);
+            result.add(current);
+            prev = current;
+        } else if (!prevBlocked && !currentBlocked && !nextBlocked) {
+            // Tricky use case, bail
+            return null;
+        } else {
+            throw new ShouldntHappenException("Marker exception for code clarity");
+        }
+
+        return prev;
+    }
+
+    private static final Ordering<BlockingState> BLOCKING_STATE_ORDERING_WITH_TIES_UNHANDLED = Ordering.<BlockingState>from(new Comparator<BlockingState>() {
         @Override
         public int compare(final BlockingState o1, final BlockingState o2) {
             // effective_date column NOT NULL
@@ -74,23 +167,7 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
                 if (blockableIdComparison != 0) {
                     return blockableIdComparison;
                 } else {
-                    // Same date, same blockable id - make sure billing transitions are respected first (assume block -> clear transitions)
-                    if (!o1.isBlockBilling() && o2.isBlockBilling()) {
-                        return 1;
-                    } else if (o1.isBlockBilling() && !o2.isBlockBilling()) {
-                        return -1;
-                    }
-
-                    // Then respect other blocking states
-                    if ((!o1.isBlockChange() && o2.isBlockChange()) ||
-                        (!o1.isBlockEntitlement() && o2.isBlockEntitlement())) {
-                        return 1;
-                    } else if ((o1.isBlockChange() && !o2.isBlockChange()) ||
-                               (o1.isBlockEntitlement() && !o2.isBlockEntitlement())) {
-                        return -1;
-                    }
-
-                    // Otherwise, just respect the created date
+                    // Same date, same blockable id, just respect the created date for now (see sortedCopyOf method above)
                     return o1.getCreatedDate().compareTo(o2.getCreatedDate());
                 }
             }
@@ -164,21 +241,9 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
     }
 
     @Override
-    public List<BlockingState> getBlockingHistoryForService(final UUID blockableId, final BlockingStateType blockingStateType, final String serviceName, final InternalTenantContext context) {
-        final List<BlockingState> statesOnDisk = delegate.getBlockingHistoryForService(blockableId, blockingStateType, serviceName, context);
-        return addBlockingStatesNotOnDisk(blockableId, blockingStateType, statesOnDisk, context);
-    }
-
-    @Override
-    public List<BlockingState> getBlockingAll(final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
-        final List<BlockingState> statesOnDisk = delegate.getBlockingAll(blockableId, blockingStateType, context);
-        return addBlockingStatesNotOnDisk(blockableId, blockingStateType, statesOnDisk, context);
-    }
-
-    @Override
     public List<BlockingState> getBlockingAllForAccountRecordId(final InternalTenantContext context) {
         final List<BlockingState> statesOnDisk = delegate.getBlockingAllForAccountRecordId(context);
-        return addBlockingStatesNotOnDisk(null, null, statesOnDisk, context);
+        return addBlockingStatesNotOnDisk(statesOnDisk, context);
     }
 
     @Override
@@ -193,9 +258,7 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
 
     // Add blocking states for add-ons, which would be impacted by a future cancellation or change of their base plan
     // See DefaultEntitlement#blockAddOnsIfRequired
-    private List<BlockingState> addBlockingStatesNotOnDisk(@Nullable final UUID blockableId,
-                                                           @Nullable final BlockingStateType blockingStateType,
-                                                           final List<BlockingState> blockingStatesOnDisk,
+    private List<BlockingState> addBlockingStatesNotOnDisk(final List<BlockingState> blockingStatesOnDisk,
                                                            final InternalTenantContext context) {
         final Collection<BlockingState> blockingStatesOnDiskCopy = new LinkedList<BlockingState>(blockingStatesOnDisk);
 
@@ -203,56 +266,29 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
         final Iterable<SubscriptionBase> baseSubscriptionsToConsider;
         final Iterable<EventsStream> eventsStreams;
         try {
-            if (blockingStateType == null) {
-                // We're coming from getBlockingAllForAccountRecordId
-                final Map<UUID, List<SubscriptionBase>> subscriptions = subscriptionInternalApi.getSubscriptionsForAccount(context);
-                baseSubscriptionsToConsider = Iterables.<SubscriptionBase>filter(Iterables.<SubscriptionBase>concat(subscriptions.values()),
-                                                                                 new Predicate<SubscriptionBase>() {
-                                                                                     @Override
-                                                                                     public boolean apply(final SubscriptionBase input) {
-                                                                                         return ProductCategory.BASE.equals(input.getCategory()) &&
-                                                                                                !EntitlementState.CANCELLED.equals(input.getState());
-                                                                                     }
-                                                                                 });
-                eventsStreams = Iterables.<EventsStream>concat(eventsStreamBuilder.buildForAccount(subscriptions, context).getEventsStreams().values());
-            } else if (BlockingStateType.SUBSCRIPTION.equals(blockingStateType)) {
-                // We're coming from getBlockingHistoryForService / getBlockingAll
-                final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(blockableId, context);
-
-                // blockable id points to a subscription, but make sure it's an add-on
-                if (ProductCategory.ADD_ON.equals(subscription.getCategory())) {
-                    final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(subscription.getBundleId(), context);
-                    baseSubscriptionsToConsider = ImmutableList.<SubscriptionBase>of(baseSubscription);
-                    eventsStreams = ImmutableList.<EventsStream>of(eventsStreamBuilder.buildForEntitlement(baseSubscription, context));
-                } else {
-                    // blockable id points to a base or standalone subscription, there is nothing to do
-                    // Simply return the sorted list
-                    return BLOCKING_STATE_ORDERING.immutableSortedCopy(blockingStatesOnDisk);
-                }
-            } else {
-                // blockable id points to an account or bundle, in which case there are no extra blocking states to add
-                // Simply return the sorted list
-                return BLOCKING_STATE_ORDERING.immutableSortedCopy(blockingStatesOnDisk);
-            }
-        } catch (SubscriptionBaseApiException e) {
-            log.error("Error retrieving subscriptions for account record id " + context.getAccountRecordId(), e);
-            throw new RuntimeException(e);
+            final Map<UUID, List<SubscriptionBase>> subscriptions = subscriptionInternalApi.getSubscriptionsForAccount(context);
+            baseSubscriptionsToConsider = Iterables.<SubscriptionBase>filter(Iterables.<SubscriptionBase>concat(subscriptions.values()),
+                                                                             new Predicate<SubscriptionBase>() {
+                                                                                 @Override
+                                                                                 public boolean apply(final SubscriptionBase input) {
+                                                                                     return ProductCategory.BASE.equals(input.getCategory());
+                                                                                 }
+                                                                             });
+            eventsStreams = Iterables.<EventsStream>concat(eventsStreamBuilder.buildForAccount(subscriptions, context).getEventsStreams().values());
         } catch (EntitlementApiException e) {
             log.error("Error computing blocking states for addons for account record id " + context.getAccountRecordId(), e);
             throw new RuntimeException(e);
         }
 
-        return addBlockingStatesNotOnDisk(blockableId, blockingStateType, blockingStatesOnDiskCopy, baseSubscriptionsToConsider, eventsStreams);
+        return addBlockingStatesNotOnDisk(null, null, blockingStatesOnDiskCopy, baseSubscriptionsToConsider, eventsStreams);
     }
 
+    // Special signature for OptimizedProxyBlockingStateDao
     protected List<BlockingState> addBlockingStatesNotOnDisk(@Nullable final UUID blockableId,
                                                              @Nullable final BlockingStateType blockingStateType,
                                                              final Collection<BlockingState> blockingStatesOnDiskCopy,
                                                              final Iterable<SubscriptionBase> baseSubscriptionsToConsider,
                                                              final Iterable<EventsStream> eventsStreams) {
-        // Retrieve the cancellation blocking state on disk, if it exists (will be used later)
-        final BlockingState cancellationBlockingStateOnDisk = findEntitlementCancellationBlockingState(blockableId, blockingStatesOnDiskCopy);
-
         // Compute the blocking states not on disk for all base subscriptions
         final DateTime now = clock.getUTCNow();
         for (final SubscriptionBase baseSubscription : baseSubscriptionsToConsider) {
@@ -264,20 +300,23 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
                                                                                }
                                                                            });
 
-            // First, check to see if the base entitlement is cancelled. If so, cancel the
+            // First, check to see if the base entitlement is cancelled
             final Collection<BlockingState> blockingStatesNotOnDisk = eventsStream.computeAddonsBlockingStatesForFutureSubscriptionBaseEvents();
 
             // Inject the extra blocking states into the stream if needed
             for (final BlockingState blockingState : blockingStatesNotOnDisk) {
                 // If this entitlement is actually already cancelled, add the cancellation event we computed
                 // only if it's prior to the blocking state on disk (e.g. add-on future cancelled but base plan cancelled earlier).
-                final boolean overrideCancellationBlockingStateOnDisk = cancellationBlockingStateOnDisk != null &&
-                                                                        isEntitlementCancellationBlockingState(blockingState) &&
-                                                                        blockingState.getEffectiveDate().isBefore(cancellationBlockingStateOnDisk.getEffectiveDate());
+                BlockingState cancellationBlockingStateOnDisk = null;
+                boolean overrideCancellationBlockingStateOnDisk = false;
+                if (isEntitlementCancellationBlockingState(blockingState)) {
+                    cancellationBlockingStateOnDisk = findEntitlementCancellationBlockingState(blockingState.getBlockedId(), blockingStatesOnDiskCopy);
+                    overrideCancellationBlockingStateOnDisk = cancellationBlockingStateOnDisk != null && blockingState.getEffectiveDate().isBefore(cancellationBlockingStateOnDisk.getEffectiveDate());
+                }
 
                 if ((
                             blockingStateType == null ||
-                            // In case we're coming from getBlockingHistoryForService / getBlockingAll, make sure we don't add
+                            // In case we're coming from OptimizedProxyBlockingStateDao, make sure we don't add
                             // blocking states for other add-ons on that base subscription
                             (BlockingStateType.SUBSCRIPTION.equals(blockingStateType) && blockingState.getBlockedId().equals(blockableId))
                     ) && (
@@ -294,7 +333,7 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
         }
 
         // Return the sorted list
-        return BLOCKING_STATE_ORDERING.immutableSortedCopy(blockingStatesOnDiskCopy);
+        return sortedCopy(blockingStatesOnDiskCopy);
     }
 
     private BlockingState findEntitlementCancellationBlockingState(@Nullable final UUID blockedId, final Iterable<BlockingState> blockingStatesOnDisk) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/DefaultEventsStream.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/DefaultEventsStream.java
index 7a735cb..1b28830 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/DefaultEventsStream.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/DefaultEventsStream.java
@@ -190,6 +190,11 @@ public class DefaultEventsStream implements EventsStream {
         return getPendingEntitlementEvents(DefaultEntitlementApi.ENT_STATE_CANCELLED);
     }
 
+    @Override
+    public BlockingState getEntitlementCancellationEvent() {
+        return entitlementCancelEvent;
+    }
+
     public Collection<BlockingState> getPendingEntitlementEvents(final String... types) {
         final List<String> typeList = ImmutableList.<String>copyOf(types);
         return Collections2.<BlockingState>filter(subscriptionEntitlementStates,
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java
index be8a899..1dcf932 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java
@@ -46,6 +46,7 @@ import com.ning.billing.entitlement.api.svcs.DefaultAccountEventsStreams;
 import com.ning.billing.entitlement.block.BlockingChecker;
 import com.ning.billing.entitlement.dao.DefaultBlockingStateDao;
 import com.ning.billing.entitlement.dao.OptimizedProxyBlockingStateDao;
+import com.ning.billing.entitlement.dao.ProxyBlockingStateDao;
 import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
@@ -60,8 +61,6 @@ import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
-import static com.ning.billing.entitlement.dao.ProxyBlockingStateDao.BLOCKING_STATE_ORDERING;
-
 @Singleton
 public class EventsStreamBuilder {
 
@@ -126,10 +125,6 @@ public class EventsStreamBuilder {
             throw new EntitlementApiException(e);
         }
 
-        return buildForAccount(account, subscriptions, internalTenantContext);
-    }
-
-    private AccountEventsStreams buildForAccount(final Account account, final Map<UUID, List<SubscriptionBase>> subscriptions, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
         if (subscriptions.isEmpty()) {
             // Bail early
             return new DefaultAccountEventsStreams(account);
@@ -197,6 +192,8 @@ public class EventsStreamBuilder {
                     subscriptionBlockingStates = subscriptionBlockingStatesOnDisk;
                 } else {
                     subscriptionBlockingStates = blockingStateDao.getBlockingHistory(subscriptionBlockingStatesOnDisk,
+                                                                                     blockingStatesForAccount,
+                                                                                     account,
                                                                                      bundle,
                                                                                      baseSubscription,
                                                                                      subscription,
@@ -209,7 +206,7 @@ public class EventsStreamBuilder {
                 final Collection<BlockingState> blockingStateSet = new LinkedHashSet<BlockingState>(accountBlockingStates);
                 blockingStateSet.addAll(bundleBlockingStates);
                 blockingStateSet.addAll(subscriptionBlockingStates);
-                final List<BlockingState> blockingStates = BLOCKING_STATE_ORDERING.immutableSortedCopy(blockingStateSet);
+                final List<BlockingState> blockingStates = ProxyBlockingStateDao.sortedCopy(blockingStateSet);
 
                 final EventsStream eventStream = buildForEntitlement(account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, blockingStates, internalTenantContext);
                 entitlementsPerBundle.get(bundleId).add(eventStream);
@@ -239,45 +236,36 @@ public class EventsStreamBuilder {
             throw new EntitlementApiException(e);
         }
 
-        return buildForEntitlement(bundle, baseSubscription, subscription, allSubscriptionsForBundle, internalTenantContext);
-    }
-
-    // Special signature for ProxyBlockingStateDao to save some DAO calls
-    public EventsStream buildForEntitlement(final SubscriptionBase subscription, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
-        final SubscriptionBaseBundle bundle;
+        final Account account;
         try {
-            bundle = subscriptionInternalApi.getBundleFromId(subscription.getBundleId(), internalTenantContext);
-        } catch (SubscriptionBaseApiException e) {
+            account = accountInternalApi.getAccountById(bundle.getAccountId(), internalTenantContext);
+        } catch (AccountApiException e) {
             throw new EntitlementApiException(e);
         }
 
-        final List<SubscriptionBase> allSubscriptionsForBundle = subscriptionInternalApi.getSubscriptionsForBundle(subscription.getBundleId(), internalTenantContext);
-        return buildForEntitlement(bundle, subscription, subscription, allSubscriptionsForBundle, internalTenantContext);
+        // Retrieve the blocking states
+        final List<BlockingState> blockingStatesForAccount = defaultBlockingStateDao.getBlockingAllForAccountRecordId(internalTenantContext);
+
+        return buildForEntitlement(blockingStatesForAccount, account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, internalTenantContext);
     }
 
     // Special signature for OptimizedProxyBlockingStateDao to save some DAO calls
-    public EventsStream buildForEntitlement(final SubscriptionBaseBundle bundle,
-                                            final SubscriptionBase subscription,
+    public EventsStream buildForEntitlement(final List<BlockingState> blockingStatesForAccount,
+                                            final Account account,
+                                            final SubscriptionBaseBundle bundle,
+                                            final SubscriptionBase baseSubscription,
                                             final List<SubscriptionBase> allSubscriptionsForBundle,
                                             final InternalTenantContext internalTenantContext) throws EntitlementApiException {
-        return buildForEntitlement(bundle, subscription, subscription, allSubscriptionsForBundle, internalTenantContext);
+        return buildForEntitlement(blockingStatesForAccount, account, bundle, baseSubscription, baseSubscription, allSubscriptionsForBundle, internalTenantContext);
     }
 
-    private EventsStream buildForEntitlement(final SubscriptionBaseBundle bundle,
+    private EventsStream buildForEntitlement(final List<BlockingState> blockingStatesForAccount,
+                                             final Account account,
+                                             final SubscriptionBaseBundle bundle,
                                              @Nullable final SubscriptionBase baseSubscription,
                                              final SubscriptionBase subscription,
                                              final List<SubscriptionBase> allSubscriptionsForBundle,
                                              final InternalTenantContext internalTenantContext) throws EntitlementApiException {
-        final Account account;
-        try {
-            account = accountInternalApi.getAccountById(bundle.getAccountId(), internalTenantContext);
-        } catch (AccountApiException e) {
-            throw new EntitlementApiException(e);
-        }
-
-        // Retrieve the blocking states
-        final Collection<BlockingState> blockingStatesForAccount = defaultBlockingStateDao.getBlockingAllForAccountRecordId(internalTenantContext);
-
         // Optimization: build lookup tables for blocking states states
         final Collection<BlockingState> accountBlockingStates = new LinkedList<BlockingState>();
         final Map<UUID, List<BlockingState>> blockingStatesPerSubscription = new HashMap<UUID, List<BlockingState>>();
@@ -308,9 +296,13 @@ public class EventsStreamBuilder {
         // needed, i.e. if this EventStream is for a standalone or a base subscription
         final Collection<BlockingState> subscriptionBlockingStates;
         if (baseSubscription == null || subscription.getId().equals(baseSubscription.getId())) {
+            // Note: we come here during the recursion from OptimizedProxyBlockingStateDao#getBlockingHistory
+            // (called by blockingStateDao.getBlockingHistory below)
             subscriptionBlockingStates = subscriptionBlockingStatesOnDisk;
         } else {
             subscriptionBlockingStates = blockingStateDao.getBlockingHistory(ImmutableList.<BlockingState>copyOf(subscriptionBlockingStatesOnDisk),
+                                                                             blockingStatesForAccount,
+                                                                             account,
                                                                              bundle,
                                                                              baseSubscription,
                                                                              subscription,
@@ -322,7 +314,7 @@ public class EventsStreamBuilder {
         final Collection<BlockingState> blockingStateSet = new LinkedHashSet<BlockingState>(accountBlockingStates);
         blockingStateSet.addAll(bundleBlockingStates);
         blockingStateSet.addAll(subscriptionBlockingStates);
-        final List<BlockingState> blockingStates = BLOCKING_STATE_ORDERING.immutableSortedCopy(blockingStateSet);
+        final List<BlockingState> blockingStates = ProxyBlockingStateDao.sortedCopy(blockingStateSet);
 
         return buildForEntitlement(account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, blockingStates, internalTenantContext);
     }
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
index 973434a..62d8de4 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
@@ -92,18 +92,6 @@ and is_active
 ;
 >>
 
-getBlockingAll() ::= <<
-select
-<allTableFields()>
-from
-<tableName()>
-where blockable_id = :blockableId
-and is_active
-<AND_CHECK_TENANT()>
-<defaultOrderBy()>
-;
->>
-
 unactiveEvent() ::= <<
 update
 <tableName()>
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultEntitlementApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultEntitlementApi.java
index e2ff2d8..caa7178 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultEntitlementApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultEntitlementApi.java
@@ -62,15 +62,12 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         testListener.pushExpectedEvent(NextEvent.CREATE);
         final Entitlement addOnEntitlement = entitlementApi.addEntitlement(entitlement.getBundleId(), addOnSpec, initialDate, callContext);
         assertListenerStatus();
-
-        /*
-        // TODO It looks like we don't check if there is a future cancellation. Maybe we should?
         try {
             entitlement.uncancelEntitlement(callContext);
             Assert.fail("Entitlement hasn't been cancelled yet");
         } catch (final EntitlementApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.SUB_CANCEL_BAD_STATE.getCode());
-        }*/
+        }
 
         clock.addDays(3);
 
@@ -108,6 +105,40 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         }
     }
 
+
+    @Test(groups = "slow")
+    public void testUncancelEffectiveCancelledEntitlement() throws AccountApiException, EntitlementApiException {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        // Keep the same object for the whole test, to make sure we refresh its state before r/w calls
+        testListener.pushExpectedEvent(NextEvent.CREATE);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), initialDate, callContext);
+        assertListenerStatus();
+
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        clock.addDays(30);
+        assertListenerStatus();
+        subscriptionInternalApi.setChargedThroughDate(entitlement.getId(), clock.getUTCNow().plusMonths(1), internalCallContext);
+
+        final LocalDate entitlementCancelledDate = clock.getToday(account.getTimeZone());
+        testListener.pushExpectedEvent(NextEvent.BLOCK);
+        final Entitlement cancelledEntitlement = entitlement.cancelEntitlementWithDateOverrideBillingPolicy(clock.getToday(account.getTimeZone()), BillingActionPolicy.END_OF_TERM, callContext);
+        assertListenerStatus();
+        Assert.assertEquals(cancelledEntitlement.getEffectiveEndDate(), entitlementCancelledDate);
+
+        testListener.pushExpectedEvent(NextEvent.UNCANCEL);
+        cancelledEntitlement.uncancelEntitlement(callContext);
+        assertListenerStatus();
+
+        final Entitlement reactivatedEntitlement = entitlementApi.getEntitlementForId(cancelledEntitlement.getId(), callContext);
+        Assert.assertNull(reactivatedEntitlement.getEffectiveEndDate());
+    }
+
     @Test(groups = "slow")
     public void testCreateEntitlementWithCheck() throws AccountApiException, EntitlementApiException {
         final LocalDate initialDate = new LocalDate(2013, 8, 7);
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 5cf5539..6f27ece 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
@@ -46,15 +46,17 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
         final Account account = accountApi.createAccount(getAccountData(7), callContext);
         final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE, NextEvent.BLOCK);
-        // UUID are chosen so UUID(entitlement1) < UUID(entitlement2)
-        final Entitlement entitlement1 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.fromString("d87c78b4-c6de-4387-8a3b-4a5850dc29fc").toString(), initialDate, callContext);
-        final Entitlement entitlement2 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.fromString("c56245e7-11a5-4a41-8854-3d31e24bcdcc").toString(), initialDate, callContext);
+        final Entitlement entitlement1 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), initialDate, callContext);
+        // Sleep 1 sec so created date are apparts from each other and ordering in the bundle does not default on the UUID which is random.
+        try {Thread.sleep(1000); } catch (InterruptedException ignore) {};
+        final Entitlement entitlement2 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), initialDate, callContext);
         entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, "stateName", "service", false, false, false, clock.getUTCNow()),
                                                                         internalCallContextFactory.createInternalCallContext(account.getId(), callContext));
         assertListenerStatus();
 
         final List<SubscriptionBundle> bundles = subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), callContext);
         Assert.assertEquals(bundles.size(), 2);
+
         // This will test the ordering as well
         subscriptionBundleChecker(bundles, initialDate, entitlement1, 0);
         subscriptionBundleChecker(bundles, initialDate, entitlement2, 1);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java b/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
index 3e3c69d..5f1faf6 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
@@ -42,11 +42,11 @@ public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDa
     private final Map<UUID, List<BlockingState>> blockingStates = new HashMap<UUID, List<BlockingState>>();
     private final Map<Long, List<BlockingState>> blockingStatesPerAccountRecordId = new HashMap<Long, List<BlockingState>>();
 
-    // TODO This mock class should also check that events are past or present except for getBlockingAll
+    // TODO This mock class should also check that events are past or present
 
     @Override
     public BlockingState getBlockingStateForService(final UUID blockableId, final BlockingStateType blockingStateType, final String serviceName, final InternalTenantContext context) {
-        final List<BlockingState> states = getBlockingAll(blockableId, blockingStateType, context);
+        final List<BlockingState> states = blockingStates.get(blockableId);
         if (states == null) {
             return null;
         }
@@ -77,30 +77,6 @@ public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDa
     }
 
     @Override
-    public List<BlockingState> getBlockingHistoryForService(final UUID overdueableId, final BlockingStateType blockingStateType, final String serviceName, final InternalTenantContext context) {
-        final List<BlockingState> states = blockingStates.get(overdueableId);
-        if (states == null) {
-            return new ArrayList<BlockingState>();
-        }
-        final ImmutableList<BlockingState> filtered = ImmutableList.<BlockingState>copyOf(Collections2.filter(states, new Predicate<BlockingState>() {
-            @Override
-            public boolean apply(@Nullable final BlockingState input) {
-                return input.getService().equals(serviceName);
-            }
-        }));
-
-        // Note! The returned list cannot be immutable!
-        return states == null ? new ArrayList<BlockingState>() : new ArrayList<BlockingState>(filtered);
-    }
-
-    @Override
-    public List<BlockingState> getBlockingAll(final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
-        final List<BlockingState> states = blockingStates.get(blockableId);
-        // Note! The returned list cannot be immutable!
-        return states == null ? new ArrayList<BlockingState>() : states;
-    }
-
-    @Override
     public List<BlockingState> getBlockingAllForAccountRecordId(final InternalTenantContext context) {
         return Objects.firstNonNull(blockingStatesPerAccountRecordId.get(context.getAccountRecordId()), ImmutableList.<BlockingState>of());
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestBlockingDao.java b/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestBlockingDao.java
index db37ccd..264e810 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestBlockingDao.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestBlockingDao.java
@@ -21,8 +21,10 @@ import java.util.UUID;
 
 import org.joda.time.LocalDate;
 import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.ning.billing.account.api.Account;
 import com.ning.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
 import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
@@ -30,9 +32,16 @@ import com.ning.billing.junction.DefaultBlockingState;
 
 public class TestBlockingDao extends EntitlementTestSuiteWithEmbeddedDB {
 
-    @Test(groups = "slow")
-    public void testDao() {
+    @BeforeMethod(groups = "slow")
+    public void setUp() throws Exception {
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
 
+        // Override the context with the right account record id
+        internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+    }
+
+    @Test(groups = "slow", description = "Check BlockingStateDao with a single service")
+    public void testDaoWithOneService() {
         final UUID uuid = UUID.randomUUID();
         final String overdueStateName = "WayPassedItMan";
         final String service = "TEST";
@@ -54,15 +63,15 @@ public class TestBlockingDao extends EntitlementTestSuiteWithEmbeddedDB {
 
         Assert.assertEquals(blockingStateDao.getBlockingStateForService(uuid, BlockingStateType.ACCOUNT, service, internalCallContext).getStateName(), state2.getStateName());
 
-        final List<BlockingState> states = blockingStateDao.getBlockingHistoryForService(uuid, BlockingStateType.ACCOUNT, service, internalCallContext);
+        final List<BlockingState> states = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(states.size(), 2);
 
         Assert.assertEquals(states.get(0).getStateName(), overdueStateName);
         Assert.assertEquals(states.get(1).getStateName(), overdueStateName2);
     }
 
-    @Test(groups = "slow")
-    public void testDaoHistory() throws Exception {
+    @Test(groups = "slow", description = "Check BlockingStateDao with multiple services")
+    public void testDaoWithMultipleServices() throws Exception {
         final UUID uuid = UUID.randomUUID();
         final String overdueStateName = "WayPassedItMan";
         final String service1 = "TEST";
@@ -81,7 +90,7 @@ public class TestBlockingDao extends EntitlementTestSuiteWithEmbeddedDB {
         final BlockingState state2 = new DefaultBlockingState(uuid, BlockingStateType.ACCOUNT, overdueStateName2, service2, blockChange, blockEntitlement, blockBilling, clock.getUTCNow());
         blockingStateDao.setBlockingState(state2, clock, internalCallContext);
 
-        final List<BlockingState> history2 = blockingStateDao.getBlockingAll(uuid, BlockingStateType.ACCOUNT, internalCallContext);
+        final List<BlockingState> history2 = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(history2.size(), 2);
         Assert.assertEquals(history2.get(0).getStateName(), overdueStateName);
         Assert.assertEquals(history2.get(1).getStateName(), overdueStateName2);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java b/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java
index 9300fc7..06989c9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java
@@ -22,6 +22,7 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
@@ -38,12 +39,21 @@ import com.ning.billing.junction.DefaultBlockingState;
 
 public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbeddedDB {
 
+    private Account account;
+
+    @BeforeMethod(groups = "slow")
+    public void setUp() throws Exception {
+        account = accountApi.createAccount(getAccountData(7), callContext);
+
+        // Override the context with the right account record id
+        internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+    }
+
     @Test(groups = "slow", description = "Verify we don't insert extra add-on events")
     public void testUnnecessaryEventsAreNotAdded() throws Exception {
         // This is a simple smoke test at the dao level only to make sure we do sane
         // things in case there are no future add-on cancellation events to add in the stream.
         // See TestEntitlementUtils for a more comprehensive test
-        final Account account = accountApi.createAccount(getAccountData(7), callContext);
         final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         testListener.pushExpectedEvent(NextEvent.CREATE);
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), clock.getUTCToday(), callContext);
@@ -54,14 +64,14 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         final String service = "service";
 
         // Verify initial state
-        Assert.assertEquals(blockingStateDao.getBlockingAll(entitlement.getId(), type, internalCallContext).size(), 0);
+        Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 0);
 
         // Set a state
         final DateTime stateDateTime = new DateTime(2013, 5, 6, 10, 11, 12, DateTimeZone.UTC);
         final BlockingState blockingState = new DefaultBlockingState(entitlement.getId(), type, state, service, false, false, false, stateDateTime);
         blockingStateDao.setBlockingState(blockingState, clock, internalCallContext);
 
-        Assert.assertEquals(blockingStateDao.getBlockingAll(entitlement.getId(), type, internalCallContext).size(), 1);
+        Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 1);
     }
 
     // See https://github.com/killbill/killbill/issues/111
@@ -75,7 +85,7 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         final String serviceB = "service-B";
 
         // Verify initial state
-        Assert.assertEquals(blockingStateDao.getBlockingAll(blockableId, type, internalCallContext).size(), 0);
+        Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 0);
 
         // Note: the checkers below rely on record_id ordering, not effective date
 
@@ -83,7 +93,7 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         final DateTime stateDateTime = new DateTime(2013, 5, 6, 10, 11, 12, DateTimeZone.UTC);
         final BlockingState blockingState1 = new DefaultBlockingState(blockableId, type, state, serviceA, false, false, false, stateDateTime);
         blockingStateDao.setBlockingState(blockingState1, clock, internalCallContext);
-        final List<BlockingState> blockingStates1 = blockingStateDao.getBlockingAll(blockableId, type, internalCallContext);
+        final List<BlockingState> blockingStates1 = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(blockingStates1.size(), 1);
         Assert.assertEquals(blockingStates1.get(0).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates1.get(0).getStateName(), state);
@@ -92,7 +102,7 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
 
         // Set the same state again - no change
         blockingStateDao.setBlockingState(blockingState1, clock, internalCallContext);
-        final List<BlockingState> blockingStates2 = blockingStateDao.getBlockingAll(blockableId, type, internalCallContext);
+        final List<BlockingState> blockingStates2 = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(blockingStates2.size(), 1);
         Assert.assertEquals(blockingStates2.get(0).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates2.get(0).getStateName(), state);
@@ -102,7 +112,7 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         // Set the state for service B
         final BlockingState blockingState2 = new DefaultBlockingState(blockableId, type, state, serviceB, false, false, false, stateDateTime);
         blockingStateDao.setBlockingState(blockingState2, clock, internalCallContext);
-        final List<BlockingState> blockingStates3 = blockingStateDao.getBlockingAll(blockableId, type, internalCallContext);
+        final List<BlockingState> blockingStates3 = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(blockingStates3.size(), 2);
         Assert.assertEquals(blockingStates3.get(0).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates3.get(0).getStateName(), state);
@@ -117,7 +127,7 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         final DateTime stateDateTime2 = new DateTime(2013, 6, 6, 10, 11, 12, DateTimeZone.UTC);
         final BlockingState blockingState3 = new DefaultBlockingState(blockableId, type, state, serviceA, false, false, false, stateDateTime2);
         blockingStateDao.setBlockingState(blockingState3, clock, internalCallContext);
-        final List<BlockingState> blockingStates4 = blockingStateDao.getBlockingAll(blockableId, type, internalCallContext);
+        final List<BlockingState> blockingStates4 = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(blockingStates4.size(), 2);
         Assert.assertEquals(blockingStates4.get(0).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates4.get(0).getStateName(), state);
@@ -132,7 +142,7 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         final DateTime stateDateTime3 = new DateTime(2013, 2, 6, 10, 11, 12, DateTimeZone.UTC);
         final BlockingState blockingState4 = new DefaultBlockingState(blockableId, type, state, serviceA, false, false, false, stateDateTime3);
         blockingStateDao.setBlockingState(blockingState4, clock, internalCallContext);
-        final List<BlockingState> blockingStates5 = blockingStateDao.getBlockingAll(blockableId, type, internalCallContext);
+        final List<BlockingState> blockingStates5 = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(blockingStates5.size(), 2);
         Assert.assertEquals(blockingStates5.get(0).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates5.get(0).getStateName(), state);
@@ -147,7 +157,7 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         final DateTime state2DateTime = new DateTime(2013, 12, 6, 10, 11, 12, DateTimeZone.UTC);
         final BlockingState blockingState5 = new DefaultBlockingState(blockableId, type, state2, serviceA, false, false, false, state2DateTime);
         blockingStateDao.setBlockingState(blockingState5, clock, internalCallContext);
-        final List<BlockingState> blockingStates6 = blockingStateDao.getBlockingAll(blockableId, type, internalCallContext);
+        final List<BlockingState> blockingStates6 = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(blockingStates6.size(), 3);
         Assert.assertEquals(blockingStates6.get(0).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates6.get(0).getStateName(), state);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
index b623448..034a147 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
@@ -18,6 +18,7 @@ package com.ning.billing.entitlement.engine.core;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.UUID;
 
 import javax.annotation.Nullable;
 
@@ -49,6 +50,7 @@ import com.ning.billing.entitlement.dao.BlockingStateSqlDao;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
 public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
@@ -279,7 +281,7 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
         // Verify the blocking states DAO adds events not on disk for the first add-on...
         checkBlockingStatesDAO(changedBaseEntitlement, addOnEntitlement, baseEffectiveCancellationOrChangeDate, false);
         // ...but not for the second one
-        final List<BlockingState> blockingStatesForSecondAddOn = blockingStateDao.getBlockingAll(secondAddOnEntitlement.getId(), BlockingStateType.SUBSCRIPTION, internalCallContext);
+        final List<BlockingState> blockingStatesForSecondAddOn = blockingStatesForBlockedId(secondAddOnEntitlement.getId());
         Assert.assertEquals(blockingStatesForSecondAddOn.size(), 0);
     }
 
@@ -417,7 +419,7 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
 
     // Test the DAO
     private void checkBlockingStatesDAO(final DefaultEntitlement baseEntitlement, final DefaultEntitlement addOnEntitlement, final LocalDate effectiveBaseCancellationDate, final LocalDate effectiveAddOnCancellationDate, final boolean isBaseCancelled) {
-        final List<BlockingState> blockingStatesForBaseEntitlement = blockingStateDao.getBlockingAll(baseEntitlement.getId(), BlockingStateType.SUBSCRIPTION, internalCallContext);
+        final List<BlockingState> blockingStatesForBaseEntitlement = blockingStatesForBlockedId(baseEntitlement.getId());
         Assert.assertEquals(blockingStatesForBaseEntitlement.size(), isBaseCancelled ? 1 : 0);
         if (isBaseCancelled) {
             Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getBlockedId(), baseEntitlement.getId());
@@ -427,7 +429,7 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
             Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);
         }
 
-        final List<BlockingState> blockingStatesForAddOn = blockingStateDao.getBlockingAll(addOnEntitlement.getId(), BlockingStateType.SUBSCRIPTION, internalCallContext);
+        final List<BlockingState> blockingStatesForAddOn = blockingStatesForBlockedId(addOnEntitlement.getId());
         Assert.assertEquals(blockingStatesForAddOn.size(), 1);
         Assert.assertEquals(blockingStatesForAddOn.get(0).getBlockedId(), addOnEntitlement.getId());
         Assert.assertEquals(blockingStatesForAddOn.get(0).getEffectiveDate().toLocalDate(), effectiveAddOnCancellationDate);
@@ -469,4 +471,14 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
                                                                        });
         return eventsStream.computeAddonsBlockingStatesForNextSubscriptionBaseEvent(effectiveDate);
     }
+
+    private List<BlockingState> blockingStatesForBlockedId(final UUID blockedId) {
+        return ImmutableList.<BlockingState>copyOf(Iterables.<BlockingState>filter(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext),
+                                                                                   new Predicate<BlockingState>() {
+                                                                                       @Override
+                                                                                       public boolean apply(final BlockingState input) {
+                                                                                           return input.getBlockedId().equals(blockedId);
+                                                                                       }
+                                                                                   }));
+    }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
index 506278a..2c94cb9 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
@@ -169,11 +169,11 @@ public class InvoiceDispatcher {
 
             // Make sure to first set the BCD if needed then get the account object (to have the BCD set)
             final BillingEventSet billingEvents = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId, context);
-            final Account account = accountApi.getAccountById(accountId, context);
 
+            final Account account = accountApi.getAccountById(accountId, context);
             final DateAndTimeZoneContext dateAndTimeZoneContext = billingEvents.iterator().hasNext() ?
                                                                   new DateAndTimeZoneContext(billingEvents.iterator().next().getEffectiveDate(), account.getTimeZone(), clock) :
-                                                                  new DateAndTimeZoneContext(null, account.getTimeZone(), clock);
+                                                                  null;
 
 
             List<Invoice> invoices = new ArrayList<Invoice>();
@@ -189,8 +189,8 @@ public class InvoiceDispatcher {
 
             final Currency targetCurrency = account.getCurrency();
 
-            final LocalDate targetDate = dateAndTimeZoneContext.computeTargetDate(targetDateTime);
-            final Invoice invoice = generator.generateInvoice(accountId, billingEvents, invoices, targetDate, targetCurrency);
+            final LocalDate targetDate = dateAndTimeZoneContext != null ? dateAndTimeZoneContext.computeTargetDate(targetDateTime) : null;
+            final Invoice invoice = targetDate != null ? generator.generateInvoice(accountId, billingEvents, invoices, targetDate, targetCurrency) : null;
             if (invoice == null) {
                 log.info("Generated null invoice for accountId {} and targetDate {} (targetDateTime {})", new Object[]{accountId, targetDate, targetDateTime});
                 if (!dryRun) {
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 03d7450..728bcce 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -194,7 +194,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
         final LocalDate endDate = new LocalDate("2012-11-26");
 
 
-        ((ClockMock) clock).setTime(new DateTime(2012, 10, 26, 1, 12, 23, DateTimeZone.UTC));
+        ((ClockMock) clock).setTime(new DateTime(2012, 10, 13, 1, 12, 23, DateTimeZone.UTC));
 
         final DateAndTimeZoneContext dateAndTimeZoneContext = new DateAndTimeZoneContext(clock.getUTCNow(), DateTimeZone.forID("Pacific/Pitcairn"), clock);
 
@@ -217,10 +217,6 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
         final LocalDate receivedTargetDate = new LocalDate(receivedDate, DateTimeZone.forID("Pacific/Pitcairn"));
         Assert.assertEquals(receivedTargetDate, endDate);
 
-        Assert.assertTrue(receivedDate.compareTo(new DateTime(2012, 11, 26, 9 /* 1 + 8 for Pitcairn */, 12, 23, DateTimeZone.UTC)) >= 0);
-        Assert.assertTrue(receivedDate.compareTo(new DateTime(2012, 11, 26, 9, 13, 0, DateTimeZone.UTC)) <= 0);
-
+        Assert.assertTrue(receivedDate.compareTo(new DateTime(2012, 11, 27, 1, 12, 23, DateTimeZone.UTC)) <= 0);
     }
-
-    //MDW add a test to cover when the account auto-invoice-off tag is present
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/RefundJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/RefundJson.java
index 73a83dd..1c17625 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/RefundJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/RefundJson.java
@@ -42,6 +42,7 @@ public class RefundJson extends JsonBase {
     private final Boolean isAdjusted;
     private final DateTime requestedDate;
     private final DateTime effectiveDate;
+    private final String status;
     private final List<InvoiceItemJson> adjustments;
 
     @JsonCreator
@@ -49,6 +50,7 @@ public class RefundJson extends JsonBase {
                       @JsonProperty("paymentId") final String paymentId,
                       @JsonProperty("amount") final BigDecimal amount,
                       @JsonProperty("currency") final String currency,
+                      @JsonProperty("status") final String status,
                       @JsonProperty("adjusted") final Boolean isAdjusted,
                       @JsonProperty("requestedDate") final DateTime requestedDate,
                       @JsonProperty("effectiveDate") final DateTime effectiveDate,
@@ -59,6 +61,7 @@ public class RefundJson extends JsonBase {
         this.paymentId = paymentId;
         this.amount = amount;
         this.currency = currency;
+        this.status = status;
         this.isAdjusted = isAdjusted;
         this.requestedDate = requestedDate;
         this.effectiveDate = effectiveDate;
@@ -71,7 +74,7 @@ public class RefundJson extends JsonBase {
 
     public RefundJson(final Refund refund, @Nullable final List<InvoiceItem> adjustments, @Nullable final List<AuditLog> auditLogs) {
         this(refund.getId().toString(), refund.getPaymentId().toString(), refund.getRefundAmount(), refund.getCurrency().toString(),
-             refund.isAdjusted(), refund.getEffectiveDate(), refund.getEffectiveDate(),
+             refund.getRefundStatus().toString(), refund.isAdjusted(), refund.getEffectiveDate(), refund.getEffectiveDate(),
              adjustments == null ? null : ImmutableList.<InvoiceItemJson>copyOf(Collections2.transform(adjustments, new Function<InvoiceItem, InvoiceItemJson>() {
                  @Override
                  public InvoiceItemJson apply(@Nullable final InvoiceItem input) {
@@ -113,6 +116,8 @@ public class RefundJson extends JsonBase {
         return adjustments;
     }
 
+    public String getStatus() { return status; }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
@@ -121,6 +126,7 @@ public class RefundJson extends JsonBase {
         sb.append(", paymentId='").append(paymentId).append('\'');
         sb.append(", amount=").append(amount);
         sb.append(", currency=").append(currency);
+        sb.append(", status=").append(status);
         sb.append(", isAdjusted=").append(isAdjusted);
         sb.append(", requestedDate=").append(requestedDate);
         sb.append(", effectiveDate=").append(effectiveDate);
@@ -135,6 +141,7 @@ public class RefundJson extends JsonBase {
         result = 31 * result + (paymentId != null ? paymentId.hashCode() : 0);
         result = 31 * result + (amount != null ? amount.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (status != null ? status.hashCode() : 0);
         result = 31 * result + (isAdjusted != null ? isAdjusted.hashCode() : 0);
         result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
         result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
@@ -222,6 +229,14 @@ public class RefundJson extends JsonBase {
             return false;
         }
 
+        if (status == null) {
+            if (other.status != null) {
+                return false;
+            }
+        } else if (!status.equals(other.status)) {
+            return false;
+        }
+
         if (adjustments == null) {
             if (other.adjustments != null) {
                 return false;
diff --git a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestRefundJson.java b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestRefundJson.java
index 544c23d..6e4e71a 100644
--- a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestRefundJson.java
+++ b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestRefundJson.java
@@ -27,6 +27,7 @@ import org.testng.annotations.Test;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.jaxrs.JaxrsTestSuiteNoDB;
+import com.ning.billing.payment.api.RefundStatus;
 
 import com.google.common.collect.ImmutableList;
 
@@ -43,9 +44,10 @@ public class TestRefundJson extends JaxrsTestSuiteNoDB {
         final boolean isAdjusted = true;
         final DateTime requestedDate = clock.getUTCNow();
         final DateTime effectiveDate = clock.getUTCNow();
+        final RefundStatus status = RefundStatus.COMPLETED;
         final List<InvoiceItemJson> adjustments = ImmutableList.<InvoiceItemJson>of(createInvoiceItemJson());
         final List<AuditLogJson> auditLogs = createAuditLogsJson(clock.getUTCNow());
-        final RefundJson refundJson = new RefundJson(refundId, paymentId, amount, currency, isAdjusted, requestedDate,
+        final RefundJson refundJson = new RefundJson(refundId, paymentId, amount, currency, status.toString(), isAdjusted, requestedDate,
                                                      effectiveDate, adjustments, auditLogs);
         Assert.assertEquals(refundJson.getRefundId(), refundId);
         Assert.assertEquals(refundJson.getPaymentId(), paymentId);
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
index dbf0294..71a5b22 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
@@ -711,7 +711,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertEquals(pairs.get(0).getEnd(), now.plusDays(4));
 
         // Verify ordering at the same effective date doesn't matter. This is to work around nondeterministic ordering
-        // behavior in ProxyBlockingStateDao#BLOCKING_STATE_ORDERING. See also TestDefaultInternalBillingApi.
+        // behavior in ProxyBlockingStateDao#BLOCKING_STATE_ORDERING_WITH_TIES_UNHANDLED. See also TestDefaultInternalBillingApi.
         blockingEvents = new ArrayList<BlockingState>();
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)));
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
index 6aac83a..e99269a 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
@@ -48,7 +48,7 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
     //
     // Pierre, why do we have invocationCount > 0 here?
     //
-    // This test will exercise ProxyBlockingStateDao#BLOCKING_STATE_ORDERING - unfortunately, for some reason,
+    // This test will exercise ProxyBlockingStateDao#BLOCKING_STATE_ORDERING_WITH_TIES_UNHANDLED - unfortunately, for some reason,
     // the ordering doesn't seem deterministic. In some scenarii,
     // BlockingState(idA, effectiveDate1, BLOCK), BlockingState(idA, effectiveDate2, CLEAR), BlockingState(idB, effectiveDate2, BLOCK), BlockingState(idB, effectiveDate3, CLEAR)
     // is ordered
@@ -200,4 +200,70 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
         Assert.assertEquals(events.get(6).getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
         Assert.assertEquals(events.get(6).getEffectiveDate(), block5Date);
     }
+
+    // See https://github.com/killbill/killbill/commit/92042843e38a67f75495b207385e4c1f9ca60990#commitcomment-4749967
+    @Test(groups = "slow", description = "Check unblock then block states with same effective date are correctly handled", invocationCount = 10)
+    public void testUnblockThenBlockBlockingStatesWithSameEffectiveDate() throws Exception {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+
+        testListener.pushExpectedEvent(NextEvent.CREATE);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), initialDate, callContext);
+        final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(entitlement.getId(), internalCallContext);
+        assertListenerStatus();
+
+        final DateTime block1Date = clock.getUTCNow();
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        final DefaultBlockingState state1 = new DefaultBlockingState(account.getId(),
+                                                                     BlockingStateType.ACCOUNT,
+                                                                     DefaultEntitlementApi.ENT_STATE_BLOCKED,
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     true,
+                                                                     true,
+                                                                     true,
+                                                                     block1Date);
+        blockingInternalApi.setBlockingState(state1, internalCallContext);
+
+        clock.addDays(1);
+
+        final DateTime block2Date = clock.getUTCNow();
+        testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
+        final DefaultBlockingState state2 = new DefaultBlockingState(account.getId(),
+                                                                     BlockingStateType.ACCOUNT,
+                                                                     DefaultEntitlementApi.ENT_STATE_CLEAR,
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     false,
+                                                                     false,
+                                                                     false,
+                                                                     block2Date);
+        blockingInternalApi.setBlockingState(state2, internalCallContext);
+        // Same date
+        final DefaultBlockingState state3 = new DefaultBlockingState(account.getId(),
+                                                                     BlockingStateType.ACCOUNT,
+                                                                     DefaultEntitlementApi.ENT_STATE_BLOCKED,
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     true,
+                                                                     true,
+                                                                     true,
+                                                                     block2Date);
+        blockingInternalApi.setBlockingState(state3, internalCallContext);
+        assertListenerStatus();
+
+        // Nothing should happen
+        clock.addDays(3);
+        assertListenerStatus();
+
+        // Expected blocking duration:
+        // * 2013-08-07 to now [2013-08-07 to 2013-08-08 then 2013-08-08 to now]
+        final List<BillingEvent> events = ImmutableList.<BillingEvent>copyOf(billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext));
+        Assert.assertEquals(events.size(), 2);
+        Assert.assertEquals(events.get(0).getTransitionType(), SubscriptionBaseTransitionType.CREATE);
+        Assert.assertEquals(events.get(0).getEffectiveDate(), subscription.getStartDate());
+        Assert.assertEquals(events.get(1).getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
+        Assert.assertEquals(events.get(1).getEffectiveDate(), block1Date);
+    }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPayment.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPayment.java
index 37bde5f..23a067b 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPayment.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPayment.java
@@ -27,12 +27,11 @@ import javax.annotation.Nullable;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.entity.EntityBase;
 import com.ning.billing.payment.dao.PaymentAttemptModelDao;
 import com.ning.billing.payment.dao.PaymentModelDao;
 import com.ning.billing.payment.dao.RefundModelDao;
-import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
 import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
-import com.ning.billing.entity.EntityBase;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultRefund.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultRefund.java
index 54f99e0..9349504 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultRefund.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultRefund.java
@@ -24,8 +24,8 @@ import javax.annotation.Nullable;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
 import com.ning.billing.entity.EntityBase;
+import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
 
 public class DefaultRefund extends EntityBase implements Refund {
 
@@ -34,16 +34,18 @@ public class DefaultRefund extends EntityBase implements Refund {
     private final Currency currency;
     private final boolean isAdjusted;
     private final DateTime effectiveDate;
+    private final RefundStatus refundStatus;
 
     public DefaultRefund(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate,
                          final UUID paymentId, final BigDecimal amount,
-                         final Currency currency, final boolean isAdjusted, final DateTime effectiveDate) {
+                         final Currency currency, final boolean isAdjusted, final DateTime effectiveDate, final RefundStatus refundStatus) {
         super(id, createdDate, updatedDate);
         this.paymentId = paymentId;
         this.amount = amount;
         this.currency = currency;
         this.isAdjusted = isAdjusted;
         this.effectiveDate = effectiveDate;
+        this.refundStatus = refundStatus;
     }
 
     @Override
@@ -72,6 +74,11 @@ public class DefaultRefund extends EntityBase implements Refund {
     }
 
     @Override
+    public RefundStatus getRefundStatus() {
+        return refundStatus;
+    }
+
+    @Override
     public RefundInfoPlugin getPluginDetail() {
         // TODO not implemented
         return null;
@@ -85,6 +92,7 @@ public class DefaultRefund extends EntityBase implements Refund {
         sb.append(", amount=").append(amount);
         sb.append(", currency=").append(currency);
         sb.append(", isAdjusted=").append(isAdjusted);
+        sb.append(", status=").append(refundStatus);
         sb.append(", effectiveDate=").append(effectiveDate);
         sb.append('}');
         return sb.toString();
@@ -113,6 +121,9 @@ public class DefaultRefund extends EntityBase implements Refund {
         if (currency != that.currency) {
             return false;
         }
+        if (refundStatus != that.refundStatus) {
+            return false;
+        }
         if (paymentId != null ? !paymentId.equals(that.paymentId) : that.paymentId != null) {
             return false;
         }
@@ -125,6 +136,7 @@ public class DefaultRefund extends EntityBase implements Refund {
         int result = paymentId != null ? paymentId.hashCode() : 0;
         result = 31 * result + (amount != null ? amount.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (refundStatus != null ? refundStatus.hashCode() : 0);
         result = 31 * result + (isAdjusted ? 1 : 0);
         result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
         return result;
diff --git a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
index 7eab0e9..a3fe237 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
@@ -43,10 +43,10 @@ import com.ning.billing.osgi.api.OSGIServiceRegistration;
 import com.ning.billing.payment.api.DefaultRefund;
 import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.Refund;
+import com.ning.billing.payment.api.RefundStatus;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.dao.PaymentModelDao;
 import com.ning.billing.payment.dao.RefundModelDao;
-import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
 import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
 import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
@@ -136,7 +136,7 @@ public class RefundProcessor extends ProcessorBase {
 
                         return new DefaultRefund(refundInfo.getId(), refundInfo.getCreatedDate(), refundInfo.getUpdatedDate(),
                                                  paymentId, refundInfo.getAmount(), account.getCurrency(),
-                                                 isAdjusted, refundInfo.getCreatedDate());
+                                                 isAdjusted, refundInfo.getCreatedDate(), RefundStatus.COMPLETED);
                     } else {
                         paymentDao.updateRefundStatus(refundInfo.getId(), RefundStatus.PLUGIN_ERRORED, refundAmount, account.getCurrency(), context);
                         throw new PaymentPluginApiException("Refund error for RefundInfo: " + refundInfo.toString(),
@@ -208,7 +208,7 @@ public class RefundProcessor extends ProcessorBase {
         }
         return new DefaultRefund(result.getId(), result.getCreatedDate(), result.getUpdatedDate(),
                                  result.getPaymentId(), result.getAmount(), result.getCurrency(),
-                                 result.isAdjusted(), result.getCreatedDate());
+                                 result.isAdjusted(), result.getCreatedDate(), result.getRefundStatus());
     }
 
     public List<Refund> getAccountRefunds(final Account account, final InternalTenantContext context)
@@ -237,7 +237,7 @@ public class RefundProcessor extends ProcessorBase {
             public Refund apply(final RefundModelDao cur) {
                 return new DefaultRefund(cur.getId(), cur.getCreatedDate(), cur.getUpdatedDate(),
                                          cur.getPaymentId(), cur.getAmount(), cur.getCurrency(),
-                                         cur.isAdjusted(), cur.getCreatedDate());
+                                         cur.isAdjusted(), cur.getCreatedDate(), cur.getRefundStatus());
             }
         }));
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
index ca5509a..ccc0e17 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
@@ -32,7 +32,7 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.clock.Clock;
 import com.ning.billing.entity.EntityPersistenceException;
 import com.ning.billing.payment.api.PaymentStatus;
-import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
+import com.ning.billing.payment.api.RefundStatus;
 import com.ning.billing.util.cache.CacheControllerDispatcher;
 import com.ning.billing.util.dao.NonEntityDao;
 import com.ning.billing.util.entity.DefaultPagination;
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
index 575e6ca..2c2e76e 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
@@ -20,13 +20,11 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
-
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.payment.api.PaymentStatus;
-import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
 import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.payment.api.RefundStatus;
 import com.ning.billing.util.entity.Pagination;
 
 public interface PaymentDao {
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java b/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java
index eb4d052..b66fd95 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java
@@ -24,9 +24,10 @@ import javax.annotation.Nullable;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.entity.EntityBase;
 import com.ning.billing.payment.api.Refund;
+import com.ning.billing.payment.api.RefundStatus;
 import com.ning.billing.util.dao.TableName;
-import com.ning.billing.entity.EntityBase;
 import com.ning.billing.util.entity.dao.EntityModelDao;
 
 public class RefundModelDao extends EntityBase implements EntityModelDao<Refund> {
@@ -100,13 +101,6 @@ public class RefundModelDao extends EntityBase implements EntityModelDao<Refund>
         return isAdjusted;
     }
 
-    public enum RefundStatus {
-        CREATED,
-        PLUGIN_COMPLETED,
-        COMPLETED,
-        PLUGIN_ERRORED
-    }
-
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
index fc75d31..2a245c6 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
@@ -30,7 +30,7 @@ import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.api.PaymentStatus;
-import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
+import com.ning.billing.payment.api.RefundStatus;
 import com.ning.billing.util.entity.Pagination;
 
 import com.google.common.collect.ImmutableList;
@@ -186,6 +186,7 @@ public class MockPaymentDao implements PaymentDao {
         return null;
     }
 
+
     @Override
     public void updateRefundStatus(final UUID refundId, final RefundStatus status, final BigDecimal processedAmount, final Currency processedCurrency, final InternalCallContext context) {
         return;
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
index 70033b8..49717e9 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
@@ -27,13 +27,12 @@ import org.testng.annotations.Test;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.PaymentTestSuiteWithEmbeddedDB;
 import com.ning.billing.payment.api.PaymentStatus;
-import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
+import com.ning.billing.payment.api.RefundStatus;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
 public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
@@ -205,7 +204,6 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(lastPayment.getEffectiveDate().compareTo(effectiveDate), 0);
         assertEquals(lastPayment.getPaymentStatus(), PaymentStatus.UNKNOWN);
 
-
         clock.addDays(3);
         final DateTime newEffectiveDate = clock.getUTCNow();
         final UUID newPaymentMethodId = UUID.randomUUID();

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 3453a84..220af20 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.5.6</version>
+        <version>0.5.7</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.8.5-SNAPSHOT</version>
diff --git a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
index baecbc6..5126a79 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
@@ -59,6 +59,7 @@ import com.ning.billing.jaxrs.json.RefundJson;
 import com.ning.billing.jaxrs.json.SubscriptionJson;
 import com.ning.billing.jaxrs.json.TenantJson;
 import com.ning.billing.jaxrs.resources.JaxrsResource;
+import com.ning.billing.payment.api.RefundStatus;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.http.client.AsyncCompletionHandler;
 import com.ning.http.client.AsyncHttpClient;
@@ -370,13 +371,11 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         assertEquals(fifthResponse.getStatusCode(), javax.ws.rs.core.Response.Status.OK.getStatusCode());
     }
 
-
-
     protected SubscriptionJson createEntitlement(final String accountId, final String bundleExternalKey, final String productName, final String productCategory, final String billingPeriod, final boolean waitCompletion) throws Exception {
 
-        final SubscriptionJson input =  new SubscriptionJson(accountId, null, null, bundleExternalKey, null ,productName, productCategory,
-                                                                            billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null, null, null, null, null,
-                                                                            null, null, null);
+        final SubscriptionJson input = new SubscriptionJson(accountId, null, null, bundleExternalKey, null, productName, productCategory,
+                                                            billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null, null, null, null, null,
+                                                            null, null, null);
         String baseJson = mapper.writeValueAsString(input);
 
         final Map<String, String> queryParams = waitCompletion ? getQueryParamsForCallCompletion("5") : DEFAULT_EMPTY_QUERY;
@@ -542,25 +541,25 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         }
 
         final InvoiceItemJson adjustment = new InvoiceItemJson(invoiceItemId, null, null, accountId, null, null, null, null,
-                                                                           null, null, null, null, amount, currency, null);
+                                                               null, null, null, null, amount, currency, null);
         final String adjustmentJson = mapper.writeValueAsString(adjustment);
         final Response response = doPost(uri, adjustmentJson, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
     }
 
     protected InvoiceJson createExternalCharge(final String accountId, final BigDecimal amount, @Nullable final String bundleId,
-                                                        @Nullable final Currency currency, @Nullable final DateTime requestedDate, final Boolean autoPay) throws Exception {
+                                               @Nullable final Currency currency, @Nullable final DateTime requestedDate, final Boolean autoPay) throws Exception {
         return doCreateExternalCharge(accountId, null, bundleId, amount, currency, requestedDate, autoPay, JaxrsResource.CHARGES_PATH);
     }
 
     protected InvoiceJson createExternalChargeForInvoice(final String accountId, final String invoiceId, @Nullable final String bundleId, final BigDecimal amount,
-                                                                  @Nullable final Currency currency, @Nullable final DateTime requestedDate, final Boolean autoPay) throws Exception {
+                                                         @Nullable final Currency currency, @Nullable final DateTime requestedDate, final Boolean autoPay) throws Exception {
         final String uri = JaxrsResource.INVOICES_PATH + "/" + invoiceId + "/" + JaxrsResource.CHARGES;
         return doCreateExternalCharge(accountId, invoiceId, bundleId, amount, currency, requestedDate, autoPay, uri);
     }
 
     private InvoiceJson doCreateExternalCharge(final String accountId, @Nullable final String invoiceId, @Nullable final String bundleId, @Nullable final BigDecimal amount,
-                                                        @Nullable final Currency currency, final DateTime requestedDate, final Boolean autoPay, final String uri) throws IOException {
+                                               @Nullable final Currency currency, final DateTime requestedDate, final Boolean autoPay, final String uri) throws IOException {
         final Map<String, String> queryParams = new HashMap<String, String>();
         if (requestedDate != null) {
             queryParams.put(JaxrsResource.QUERY_REQUESTED_DT, requestedDate.toDateTimeISO().toString());
@@ -570,7 +569,7 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         }
 
         final InvoiceItemJson externalCharge = new InvoiceItemJson(null, invoiceId, null, accountId, bundleId, null, null, null,
-                                                                               null, null, null, null, amount, currency, null);
+                                                                   null, null, null, null, amount, currency, null);
         final String externalChargeJson = mapper.writeValueAsString(externalCharge);
         final Response response = doPost(uri, externalChargeJson, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
@@ -691,7 +690,7 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
 
     protected void payAllInvoices(final AccountJson accountJson, final Boolean externalPayment) throws IOException {
         final PaymentJson payment = new PaymentJson(null, null, accountJson.getAccountId(), null, null, null, null,
-                                                                null, null, 0, null, null, null, null, null, null, null, null);
+                                                    null, null, 0, null, null, null, null, null, null, null, null);
         final String postJson = mapper.writeValueAsString(payment);
 
         final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.PAYMENTS;
@@ -700,7 +699,7 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
 
     protected List<PaymentJson> createInstaPayment(final AccountJson accountJson, final InvoiceJson invoice) throws IOException {
         final PaymentJson payment = new PaymentJson(invoice.getAmount(), BigDecimal.ZERO, accountJson.getAccountId(),
-                                                                invoice.getInvoiceId(), null, null, null, null, null, 0, null, null, null, null, null, null, null, null);
+                                                    invoice.getInvoiceId(), null, null, null, null, null, 0, null, null, null, null, null, null, null, null);
         final String postJson = mapper.writeValueAsString(payment);
 
         final String uri = JaxrsResource.INVOICES_PATH + "/" + invoice.getInvoiceId() + "/" + JaxrsResource.PAYMENTS;
@@ -711,8 +710,8 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
 
     protected List<PaymentJson> createExternalPayment(final AccountJson accountJson, final String invoiceId, final BigDecimal paidAmount) throws IOException {
         final PaymentJson payment = new PaymentJson(paidAmount, BigDecimal.ZERO, accountJson.getAccountId(),
-                                                                invoiceId, null, null, null, null, null, 0,
-                                                                null, null, null, null, null, null, null, null);
+                                                    invoiceId, null, null, null, null, null, 0,
+                                                    null, null, null, null, null, null, null, null);
         final String postJson = mapper.writeValueAsString(payment);
 
         final String paymentURI = JaxrsResource.INVOICES_PATH + "/" + invoiceId + "/" + JaxrsResource.PAYMENTS;
@@ -791,9 +790,9 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         final List<InvoiceItemJson> adjustments = new ArrayList<InvoiceItemJson>();
         for (final String itemId : itemAdjustments.keySet()) {
             adjustments.add(new InvoiceItemJson(itemId, null, null, null, null, null, null, null, null, null, null, null,
-                                                      itemAdjustments.get(itemId), null, null));
+                                                itemAdjustments.get(itemId), null, null));
         }
-        final RefundJson refundJson = new RefundJson(null, paymentId, amount, DEFAULT_CURRENCY, adjusted, null, null, adjustments, null);
+        final RefundJson refundJson = new RefundJson(null, paymentId, amount, DEFAULT_CURRENCY, RefundStatus.COMPLETED.toString(), adjusted, null, null, adjustments, null);
         final String baseJson = mapper.writeValueAsString(refundJson);
         final Response response = doPost(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
@@ -867,7 +866,6 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
     // OVERDUE
     //
 
-
     protected OverdueStateJson getOverdueStateForAccount(final String accountId) throws Exception {
         final String overdueURI = JaxrsResource.ACCOUNTS_PATH + "/" + accountId + "/" + OVERDUE;
         final Response overdueResponse = doGet(overdueURI, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java b/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java
index 01200d6..321903c 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java
@@ -30,6 +30,7 @@ import com.ning.billing.jaxrs.json.InvoiceJson;
 import com.ning.billing.jaxrs.json.PaymentJson;
 import com.ning.billing.jaxrs.json.PaymentMethodJson;
 import com.ning.billing.jaxrs.json.RefundJson;
+import com.ning.billing.payment.api.RefundStatus;
 
 public class TestPayment extends TestJaxrsBase {
 
@@ -177,6 +178,7 @@ public class TestPayment extends TestJaxrsBase {
         Assert.assertEquals(refundJsonCheck.getPaymentId(), paymentJson.getPaymentId());
         Assert.assertEquals(refundJsonCheck.getAmount().setScale(2, RoundingMode.HALF_UP), refundAmount.setScale(2, RoundingMode.HALF_UP));
         Assert.assertEquals(refundJsonCheck.getCurrency(), DEFAULT_CURRENCY);
+        Assert.assertEquals(refundJsonCheck.getStatus(), RefundStatus.COMPLETED.toString());
         Assert.assertEquals(refundJsonCheck.getEffectiveDate().getYear(), clock.getUTCNow().getYear());
         Assert.assertEquals(refundJsonCheck.getEffectiveDate().getMonthOfYear(), clock.getUTCNow().getMonthOfYear());
         Assert.assertEquals(refundJsonCheck.getEffectiveDate().getDayOfMonth(), clock.getUTCNow().getDayOfMonth());
diff --git a/util/src/main/java/com/ning/billing/util/timezone/DateAndTimeZoneContext.java b/util/src/main/java/com/ning/billing/util/timezone/DateAndTimeZoneContext.java
index b9e60b1..c96a9f1 100644
--- a/util/src/main/java/com/ning/billing/util/timezone/DateAndTimeZoneContext.java
+++ b/util/src/main/java/com/ning/billing/util/timezone/DateAndTimeZoneContext.java
@@ -18,6 +18,7 @@ package com.ning.billing.util.timezone;
 
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
+import org.joda.time.Days;
 import org.joda.time.LocalDate;
 import org.joda.time.LocalTime;
 
@@ -31,6 +32,7 @@ import com.ning.billing.clock.Clock;
 public final class DateAndTimeZoneContext {
 
     private final LocalTime referenceTime;
+    private final int offsetFromUtc;
     private final DateTimeZone accountTimeZone;
     private final Clock clock;
 
@@ -38,6 +40,13 @@ public final class DateAndTimeZoneContext {
         this.clock = clock;
         this.referenceTime = effectiveDateTime != null ? effectiveDateTime.toLocalTime() : null;
         this.accountTimeZone = accountTimeZone;
+        this.offsetFromUtc = computeOffsetFromUtc(effectiveDateTime, accountTimeZone);
+    }
+
+    static int computeOffsetFromUtc(final DateTime effectiveDateTime, final DateTimeZone accountTimeZone) {
+        final LocalDate localDateInAccountTimeZone = new LocalDate(effectiveDateTime, accountTimeZone);
+        final LocalDate localDateInUTC = new LocalDate(effectiveDateTime, DateTimeZone.UTC);
+        return Days.daysBetween(localDateInUTC, localDateInAccountTimeZone).getDays();
     }
 
     public LocalDate computeTargetDate(final DateTime targetDateTime) {
@@ -50,9 +59,9 @@ public final class DateAndTimeZoneContext {
         // Since we create the targetDate for next invoice using the date from the notificationQ, we need to make sure
         // that this datetime once transformed into a LocalDate points to the correct day.
         //
-        // e.g If accountTimeZone is -8 and we want to invoice on the 16, with a toDateTimeAtCurrentTime = 00:00:23,
-        // we will generate a datetime that is 16T08:00:23 => LocalDate in that timeZone stays on the 16.
-        //
+        // All we need to do is figure is the transformation from DateTime (point in time) to LocalDate (date in account time zone)
+        // changed the day; if so, when we recompute a UTC date from LocalDate (date in account time zone), we can simply chose a reference
+        // time and apply the offset backward to end up on the right day
         //
         // We use clock.getUTCNow() to get the offset with account timezone but that may not be correct
         // when we transition from standard time and daylight saving time. We could end up with a result
@@ -60,9 +69,7 @@ public final class DateAndTimeZoneContext {
         // We will fix that by re-inserting ourselves in the notificationQ if we detect that there is no invoice
         // and yet the subscription is recurring and not cancelled.
         //
-        final int utcOffest = accountTimeZone.getOffset(clock.getUTCNow());
-        final int localToUTCOffest = -1 * utcOffest;
-        return invoiceItemEndDate.toDateTime(referenceTime, DateTimeZone.UTC).plusMillis(localToUTCOffest);
+        return invoiceItemEndDate.toDateTime(referenceTime, DateTimeZone.UTC).plusDays(-offsetFromUtc);
     }
 
     public DateTime computeUTCDateTimeFromNow() {
diff --git a/util/src/test/java/com/ning/billing/util/timezone/TestDateAndTimeZoneContext.java b/util/src/test/java/com/ning/billing/util/timezone/TestDateAndTimeZoneContext.java
new file mode 100644
index 0000000..f73a120
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/timezone/TestDateAndTimeZoneContext.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2010-2013 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.util.timezone;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+import org.testng.annotations.Test;
+
+import com.ning.billing.util.UtilTestSuiteNoDB;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+//
+// There are two categories of tests, one that test the offset calculation and one that calculates
+// how to get a DateTime from a LocalDate (in account time zone)
+//
+// Tests {1, 2, 3} use an account timezone with a negative offset (-8) and tests {A, B, C} use an account timezone with a positive offset (+8)
+//
+public class TestDateAndTimeZoneContext extends UtilTestSuiteNoDB {
+
+    private final DateTimeFormatter DATE_TIME_FORMATTER = ISODateTimeFormat.dateTimeParser();
+
+    final String effectiveDateTime1 = "2012-01-20T07:30:42.000Z";
+    final String effectiveDateTime2 = "2012-01-20T08:00:00.000Z";
+    final String effectiveDateTime3 = "2012-01-20T08:45:33.000Z";
+
+    final String effectiveDateTimeA = "2012-01-20T16:30:42.000Z";
+    final String effectiveDateTimeB = "2012-01-20T16:00:00.000Z";
+    final String effectiveDateTimeC = "2012-01-20T15:30:42.000Z";
+
+
+    //
+    // Take an negative timezone offset and a reference time that is less than the offset (07:30:42 < 8)
+    // => to expect a negative offset of one day
+    //
+    @Test(groups = "fast")
+    public void testComputeOffset1() {
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime1);
+
+        int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+        assertEquals(offset, -1);
+    }
+
+    //
+    // Take an negative timezone offset and a reference time that is equal than the offset (08:00:00 = 8)
+    // => to expect an offset of 0
+    //
+    @Test(groups = "fast")
+    public void testComputeOffset2() {
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime2);
+
+        int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+        assertEquals(offset, 0);
+    }
+
+    //
+    // Take an negative timezone offset and a reference time that is greater than the offset (08:45:33 > 8)
+    // => to expect an offset of 0
+    //
+    @Test(groups = "fast")
+    public void testComputeOffset3() {
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime3);
+
+        int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+        assertEquals(offset, 0);
+    }
+
+    //
+    // Take an positive timezone offset and a reference time that closer to the end of the day than the timezone (16:30:42 + 8 > 24)
+    // => to expect a positive offset of one day
+    //
+    @Test(groups = "fast")
+    public void testComputeOffsetA() {
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeA);
+
+        int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+        assertEquals(offset, 1);
+    }
+
+    //
+    // Take an positive timezone offset and a reference time that brings us exactly at the end of the day (16:00:00 + 8 = 24)
+    // => to expect an offset of 1
+    //
+    @Test(groups = "fast")
+    public void testComputeOffsetB() {
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeB);
+
+        int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+        assertEquals(offset, 1);
+    }
+
+    //
+    // Take an positive timezone offset and a reference time that further away to the end of the day  (15:30:42 + 8 < 24)
+    // =>  to expect an offset of 0
+    //
+    @Test(groups = "fast")
+    public void testComputeOffsetC() {
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeC);
+
+        int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+        assertEquals(offset, 0);
+    }
+
+    @Test(groups = "fast")
+    public void testComputeUTCDateTimeFromLocalDate1() {
+
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime1);
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+        final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+
+        final LocalDate endDate = new LocalDate(2013, 01, 19);
+        final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
+        assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+    }
+
+
+    @Test(groups = "fast")
+    public void testComputeUTCDateTimeFromLocalDate2() {
+
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime2);
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+        final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+
+        final LocalDate endDate = new LocalDate(2013, 01, 20);
+        final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
+        assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+    }
+
+
+    @Test(groups = "fast")
+    public void testComputeUTCDateTimeFromLocalDate3() {
+
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime3);
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+        final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+
+        final LocalDate endDate = new LocalDate(2013, 01, 20);
+        final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
+        assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+    }
+
+    @Test(groups = "fast")
+    public void testComputeUTCDateTimeFromLocalDateA() {
+
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeA);
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+        final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+
+        final LocalDate endDate = new LocalDate(2013, 01, 21);
+        final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
+        assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+    }
+
+    @Test(groups = "fast")
+    public void testComputeUTCDateTimeFromLocalDateB() {
+
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeB);
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+        final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+
+        final LocalDate endDate = new LocalDate(2013, 01, 21);
+        final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
+        assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+    }
+
+    @Test(groups = "fast")
+    public void testComputeUTCDateTimeFromLocalDateC() {
+
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeC);
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+        final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+
+        final LocalDate endDate = new LocalDate(2013, 01, 20);
+        final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
+        assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+    }
+}