killbill-uncached

Changes

account/pom.xml 2(+1 -1)

api/pom.xml 2(+1 -1)

beatrix/pom.xml 2(+1 -1)

catalog/pom.xml 2(+1 -1)

currency/pom.xml 2(+1 -1)

invoice/pom.xml 2(+1 -1)

jaxrs/pom.xml 2(+1 -1)

junction/pom.xml 2(+1 -1)

NEWS 6(+6 -0)

osgi/pom.xml 2(+1 -1)

overdue/pom.xml 2(+1 -1)

payment/pom.xml 2(+1 -1)

pom.xml 4(+2 -2)

server/pom.xml 2(+1 -1)

tenant/pom.xml 2(+1 -1)

usage/pom.xml 2(+1 -1)

util/pom.xml 2(+1 -1)

Details

account/pom.xml 2(+1 -1)

diff --git a/account/pom.xml b/account/pom.xml
index b4d7b88..b5603ba 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-account</artifactId>

api/pom.xml 2(+1 -1)

diff --git a/api/pom.xml b/api/pom.xml
index d42225d..ff7292d 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-internal-api</artifactId>

beatrix/pom.xml 2(+1 -1)

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 1ee0c59..f71aa5c 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>

catalog/pom.xml 2(+1 -1)

diff --git a/catalog/pom.xml b/catalog/pom.xml
index 82c5e90..ed64b49 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-catalog</artifactId>

currency/pom.xml 2(+1 -1)

diff --git a/currency/pom.xml b/currency/pom.xml
index 40f731b..6af9d27 100644
--- a/currency/pom.xml
+++ b/currency/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index f4d8b8a..dbee812 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
index 20148a2..c182b4c 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
@@ -25,15 +25,15 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
-import org.joda.time.LocalDate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -59,9 +59,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
 
     private final Logger logger = LoggerFactory.getLogger(DefaultSubscriptionBundleTimeline.class);
 
-    // STEPH This is added to give us confidence the timeline we generate behaves as expected. Could be removed at some point
-    private final static String TIMELINE_WARN_LOG = "Sanity Timeline: ";
-
     public static final String BILLING_SERVICE_NAME = "billing-service";
     public static final String ENT_BILLING_SERVICE_NAME = "entitlement+billing-service";
 
@@ -81,7 +78,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
         this.events = computeEvents(entitlements, new LinkedList<BlockingState>(blockingStates), accountTimeZone);
     }
 
-    public DefaultSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final List<Entitlement> entitlements, List<BlockingState> allBlockingStates) {
+    public DefaultSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final List<Entitlement> entitlements, final List<BlockingState> allBlockingStates) {
         this.accountId = accountId;
         this.bundleId = bundleId;
         this.externalKey = externalKey;
@@ -94,8 +91,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
     // - base subscription events are already ordered for each Entitlement and so when we reorder at the bundle level we try not to break that initial ordering
     // - blocking state events occur at various level (account, bundle and subscription) so for higher level, we need to dispatch that on each subscription.
     //
-    private List<SubscriptionEvent> computeEvents(final Collection<Entitlement> entitlements, List<BlockingState> allBlockingStates, final DateTimeZone accountTimeZone) {
-
+    private List<SubscriptionEvent> computeEvents(final Collection<Entitlement> entitlements, final List<BlockingState> allBlockingStates, final DateTimeZone accountTimeZone) {
         // Extract ids for all entitlement in the list
         final Set<UUID> allEntitlementUUIDs = new TreeSet(Collections2.transform(entitlements, new Function<Entitlement, UUID>() {
             @Override
@@ -140,23 +136,44 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                 if (createdDateComp != 0) {
                     return createdDateComp;
                 }
-                logger.warn(TIMELINE_WARN_LOG + "Detected two identical blockingStates events for blockableId = " + o1.getBlockedId() +
-                            ", type = " + o1.getType() + ", ");
-                // Underministic-- not sure that will ever happen. Once we are confident this never happens we should thrown IllegalException
+
+                // Non deterministic -- not sure that will ever happen. Once we are confident this never happens, we should throw ShouldntHappenException
                 return 0;
             }
         });
 
-        for (BlockingState bs : allBlockingStates) {
-
+        for (final BlockingState bs : allBlockingStates) {
             final List<SubscriptionEvent> newEvents = new ArrayList<SubscriptionEvent>();
-            int index = insertFromBlockingEvent(accountTimeZone, allEntitlementUUIDs, result, bs, bs.getEffectiveDate(), newEvents);
+            final int index = insertFromBlockingEvent(accountTimeZone, allEntitlementUUIDs, result, bs, bs.getEffectiveDate(), newEvents);
             insertAfterIndex(result, newEvents, index);
         }
+
         reOrderSubscriptionEventsOnSameDateByType(result);
+
+        removeOverlappingSubscriptionEvents(result);
+
         return result;
     }
 
+    // Make sure the argument supports the remove operation - hence expect a LinkedList, not a List
+    private void removeOverlappingSubscriptionEvents(final LinkedList<SubscriptionEvent> events) {
+        final Iterator<SubscriptionEvent> iterator = events.iterator();
+        final Map<String, DefaultSubscriptionEvent> prevPerService = new HashMap<String, DefaultSubscriptionEvent>();
+        while (iterator.hasNext()) {
+            final DefaultSubscriptionEvent current = (DefaultSubscriptionEvent) iterator.next();
+            final DefaultSubscriptionEvent prev = prevPerService.get(current.getServiceName());
+            if (prev != null) {
+                if (current.overlaps(prev)) {
+                    iterator.remove();
+                } else {
+                    prevPerService.put(current.getServiceName(), current);
+                }
+            } else {
+                prevPerService.put(current.getServiceName(), current);
+            }
+        }
+    }
+
     //
     // All events have been inserted and should be at the right place, except that we want to ensure that events for a given subscription,
     // and for a given time are ordered by SubscriptionEventType.
@@ -172,8 +189,8 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
     protected void reOrderSubscriptionEventsOnSameDateByType(final List<SubscriptionEvent> events) {
         final int size = events.size();
         for (int i = 0; i < size; i++) {
-            final DefaultSubscriptionEvent cur = (DefaultSubscriptionEvent) events.get(i);
-            final DefaultSubscriptionEvent next = (i < (size - 1)) ? (DefaultSubscriptionEvent) events.get(i + 1) : null;
+            final SubscriptionEvent cur = events.get(i);
+            final SubscriptionEvent next = (i < (size - 1)) ? events.get(i + 1) : null;
 
             final boolean shouldSwap = (next != null && shouldSwap(cur, next, true));
             final boolean shouldReverseSort = (next == null || shouldSwap);
@@ -184,8 +201,8 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
             }
             if (shouldReverseSort) {
                 while (currentIndex >= 1) {
-                    final DefaultSubscriptionEvent revCur = (DefaultSubscriptionEvent) events.get(currentIndex);
-                    final DefaultSubscriptionEvent other = (DefaultSubscriptionEvent) events.get(currentIndex - 1);
+                    final SubscriptionEvent revCur = events.get(currentIndex);
+                    final SubscriptionEvent other = events.get(currentIndex - 1);
                     if (shouldSwap(revCur, other, false)) {
                         Collections.swap(events, currentIndex, currentIndex - 1);
                     }
@@ -198,8 +215,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
         }
     }
 
-    private boolean shouldSwap(DefaultSubscriptionEvent cur, DefaultSubscriptionEvent other, boolean isAscending) {
-
+    private boolean shouldSwap(final SubscriptionEvent cur, final SubscriptionEvent other, final boolean isAscending) {
         // For a given date, order by subscriptionId, and within subscription by event type
         final int idComp = cur.getEntitlementId().compareTo(other.getEntitlementId());
         return (cur.getEffectiveDate().compareTo(other.getEffectiveDate()) == 0 &&
@@ -211,8 +227,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                    (idComp == 0 && cur.getSubscriptionEventType().ordinal() < other.getSubscriptionEventType().ordinal())))));
     }
 
-    private void insertAfterIndex(final LinkedList<SubscriptionEvent> original, final List<SubscriptionEvent> newEvents, int index) {
-
+    private void insertAfterIndex(final LinkedList<SubscriptionEvent> original, final List<SubscriptionEvent> newEvents, final int index) {
         final boolean firstPosition = (index == -1);
         final boolean lastPosition = (index == original.size() - 1);
         if (lastPosition || firstPosition) {
@@ -233,10 +248,9 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
     // reOrderSubscriptionEventsOnSameDateByType would reorder them anyway if this was not the case.
     //
     private int insertFromBlockingEvent(final DateTimeZone accountTimeZone, final Set<UUID> allEntitlementUUIDs, final List<SubscriptionEvent> result, final BlockingState bs, final DateTime bsEffectiveDate, final List<SubscriptionEvent> newEvents) {
-
         // Keep the current state per entitlement
         final Map<UUID, TargetState> targetStates = new HashMap<UUID, TargetState>();
-        for (UUID cur : allEntitlementUUIDs) {
+        for (final UUID cur : allEntitlementUUIDs) {
             targetStates.put(cur, new TargetState());
         }
 
@@ -248,7 +262,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
         // Where we need to insert in that stream
         DefaultSubscriptionEvent curInsertion = null;
         while (it.hasNext()) {
-            DefaultSubscriptionEvent cur = (DefaultSubscriptionEvent) it.next();
+            final DefaultSubscriptionEvent cur = (DefaultSubscriptionEvent) it.next();
             final int compEffectiveDate = bsEffectiveDate.compareTo(cur.getEffectiveDateTime());
             final boolean shouldContinue = (compEffectiveDate >= 0);
             if (!shouldContinue) {
@@ -283,13 +297,12 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                                                 ImmutableList.<UUID>copyOf(allEntitlementUUIDs);
 
         // For each target compute the new events that should be inserted in the stream
-        for (UUID target : targetEntitlementIds) {
-
+        for (final UUID target : targetEntitlementIds) {
             final SubscriptionEvent[] prevNext = findPrevNext(result, target, curInsertion);
             final TargetState curTargetState = targetStates.get(target);
 
             final List<SubscriptionEventType> eventTypes = curTargetState.addStateAndReturnEventTypes(bs);
-            for (SubscriptionEventType t : eventTypes) {
+            for (final SubscriptionEventType t : eventTypes) {
                 newEvents.add(toSubscriptionEvent(prevNext[0], prevNext[1], target, bs, t, accountTimeZone));
             }
         }
@@ -298,12 +311,11 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
 
     // Extract prev and next events in the stream events for that particular target subscription from the insertionEvent
     private SubscriptionEvent[] findPrevNext(final List<SubscriptionEvent> events, final UUID targetEntitlementId, final SubscriptionEvent insertionEvent) {
-
         // Find prev/next event for the same entitlement
         final SubscriptionEvent[] result = new DefaultSubscriptionEvent[2];
         if (insertionEvent == null) {
             result[0] = null;
-            result[1] = events.size() > 0 ? events.get(0) : null;
+            result[1] = !events.isEmpty() ? events.get(0) : null;
             return result;
         }
 
@@ -344,64 +356,35 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                 }
             }
         }
-        sanitizeForBaseRecreateEvents(result);
-        return result;
-    }
 
-    //
-    // Old version of code would use CANCEL/RE_CREATE to simulate PAUSE_BILLING/RESUME_BILLING
-    // (Relies on the assumption that there is no blocking_state event matching that CACNEL event so:
-    // 1. The STOP_BILLING (coming from the row CANCEL event) should be transformed into a PAUSE_BILLING
-    // 2. We also add a PAUSE_ENTITLEMENT at the same time as the PAUSE_BILLING
-    //
-    private void sanitizeForBaseRecreateEvents(final LinkedList<SubscriptionEvent> input) {
-        final Collection<UUID> guiltyEntitlementIds = new TreeSet<UUID>();
-        final ListIterator<SubscriptionEvent> it = input.listIterator(input.size());
-        while (it.hasPrevious()) {
-            final SubscriptionEvent cur = it.previous();
-            if (cur.getSubscriptionEventType() == SubscriptionEventType.RESUME_BILLING) {
-                guiltyEntitlementIds.add(cur.getEntitlementId());
-                continue;
-            }
-            if (cur.getSubscriptionEventType() == SubscriptionEventType.STOP_BILLING &&
-                guiltyEntitlementIds.contains(cur.getEntitlementId())) {
-                guiltyEntitlementIds.remove(cur.getEntitlementId());
-                final SubscriptionEvent correctedBillingEvent = new DefaultSubscriptionEvent((DefaultSubscriptionEvent) cur, SubscriptionEventType.PAUSE_BILLING);
-                it.set(correctedBillingEvent);
-
-                // Old versions of the code won't have an associated event in blocking_states - we need to add one on the fly
-                final SubscriptionEvent correctedEntitlementEvent = new DefaultSubscriptionEvent((DefaultSubscriptionEvent) cur, SubscriptionEventType.PAUSE_ENTITLEMENT);
-                it.add(correctedEntitlementEvent);
-            }
-        }
+        return result;
     }
 
-    private void insertSubscriptionEvent(final SubscriptionEvent event, final LinkedList<SubscriptionEvent> result) {
+    private void insertSubscriptionEvent(final SubscriptionEvent event, final List<SubscriptionEvent> result) {
         int index = 0;
-        for (SubscriptionEvent cur : result) {
-            int compEffectiveDate = event.getEffectiveDate().compareTo(cur.getEffectiveDate());
+        for (final SubscriptionEvent cur : result) {
+            final int compEffectiveDate = event.getEffectiveDate().compareTo(cur.getEffectiveDate());
             if (compEffectiveDate < 0) {
                 // EffectiveDate is less than cur -> insert here
                 break;
             } else if (compEffectiveDate == 0) {
-
-                int compUUID = event.getEntitlementId().compareTo(cur.getEntitlementId());
+                final int compUUID = event.getEntitlementId().compareTo(cur.getEntitlementId());
                 if (compUUID < 0) {
                     // Same EffectiveDate but subscription are different, no need top sort further just return something deterministic
                     break;
                 } else if (compUUID == 0) {
-
-                    int eventOrder = event.getSubscriptionEventType().ordinal() - cur.getSubscriptionEventType().ordinal();
+                    final int eventOrder = event.getSubscriptionEventType().ordinal() - cur.getSubscriptionEventType().ordinal();
                     if (eventOrder < 0) {
                         // Same EffectiveDate and same subscription, order by SubscriptionEventType;
                         break;
                     }
 
-                    // Two identical event for the same subscription at the same time, this sounds like some data issue
+                    // Two identical events for the same subscription in the same day, trust createdDate
                     if (eventOrder == 0) {
-                        logger.warn(TIMELINE_WARN_LOG + "Detected identical events type = " + event.getSubscriptionEventType() + " ids = " +
-                                    event.getId() + ", " + cur.getId() + " for subscription " + cur.getEntitlementId());
-                        break;
+                        final int compCreatedDate = (((DefaultSubscriptionEvent) event).getCreatedDate()).compareTo(((DefaultSubscriptionEvent) cur).getCreatedDate());
+                        if (compCreatedDate <= 0) {
+                            break;
+                        }
                     }
                 }
             }
@@ -410,7 +393,74 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
         result.add(index, event);
     }
 
-    private SubscriptionEvent toSubscriptionEvent(final SubscriptionEvent prev, final SubscriptionEvent next, final UUID entitlementId, final BlockingState in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
+    private SubscriptionEvent toSubscriptionEvent(@Nullable final SubscriptionEvent prev, @Nullable final SubscriptionEvent next,
+                                                  final UUID entitlementId, final BlockingState in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
+        final Product prevProduct;
+        final Plan prevPlan;
+        final PlanPhase prevPlanPhase;
+        final PriceList prevPriceList;
+        final BillingPeriod prevBillingPeriod;
+        // Enforce prev = null for start events
+        if (prev == null || SubscriptionEventType.START_ENTITLEMENT.equals(eventType) || SubscriptionEventType.START_BILLING.equals(eventType)) {
+            prevProduct = null;
+            prevPlan = null;
+            prevPlanPhase = null;
+            prevPriceList = null;
+            prevBillingPeriod = null;
+        } else {
+            // We look for the next for the 'prev' meaning we we are headed to, but if this is null -- for example on cancellation we get the prev which gives the correct state.
+            prevProduct = (prev.getNextProduct() != null ? prev.getNextProduct() : prev.getPrevProduct());
+            prevPlan = (prev.getNextPlan() != null ? prev.getNextPlan() : prev.getPrevPlan());
+            prevPlanPhase = (prev.getNextPhase() != null ? prev.getNextPhase() : prev.getPrevPhase());
+            prevPriceList = (prev.getNextPriceList() != null ? prev.getNextPriceList() : prev.getPrevPriceList());
+            prevBillingPeriod = (prev.getNextBillingPeriod() != null ? prev.getNextBillingPeriod() : prev.getPrevBillingPeriod());
+        }
+
+        final Product nextProduct;
+        final Plan nextPlan;
+        final PlanPhase nextPlanPhase;
+        final PriceList nextPriceList;
+        final BillingPeriod nextBillingPeriod;
+        if (SubscriptionEventType.PAUSE_ENTITLEMENT.equals(eventType) || SubscriptionEventType.PAUSE_BILLING.equals(eventType) ||
+            SubscriptionEventType.RESUME_ENTITLEMENT.equals(eventType) || SubscriptionEventType.RESUME_BILLING.equals(eventType) ||
+            (SubscriptionEventType.SERVICE_STATE_CHANGE.equals(eventType) && (prev == null || (!SubscriptionEventType.STOP_ENTITLEMENT.equals(prev.getSubscriptionEventType()) && !SubscriptionEventType.STOP_BILLING.equals(prev.getSubscriptionEventType()))))) {
+            // Enforce next = prev for pause/resume events as well as service changes
+            nextProduct = prevProduct;
+            nextPlan = prevPlan;
+            nextPlanPhase = prevPlanPhase;
+            nextPriceList = prevPriceList;
+            nextBillingPeriod = prevBillingPeriod;
+        } else if (next == null) {
+            // Enforce next = null for stop events
+            if (prev == null || SubscriptionEventType.STOP_ENTITLEMENT.equals(eventType) || SubscriptionEventType.STOP_BILLING.equals(eventType)) {
+                nextProduct = null;
+                nextPlan = null;
+                nextPlanPhase = null;
+                nextPriceList = null;
+                nextBillingPeriod = null;
+            } else {
+                nextProduct = prev.getNextProduct();
+                nextPlan = prev.getNextPlan();
+                nextPlanPhase = prev.getNextPhase();
+                nextPriceList = prev.getNextPriceList();
+                nextBillingPeriod = prev.getNextBillingPeriod();
+            }
+        } else {
+            nextProduct = next.getNextProduct();
+            nextPlan = next.getNextPlan();
+            nextPlanPhase = next.getNextPhase();
+            nextPriceList = next.getNextPriceList();
+            nextBillingPeriod = next.getNextBillingPeriod();
+        }
+
+        // See https://github.com/killbill/killbill/issues/135
+        final String serviceName;
+        if (DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME.equals(in.getService())) {
+            serviceName = getServiceName(eventType);
+        } else {
+            serviceName = in.getService();
+        }
+
         return new DefaultSubscriptionEvent(in.getId(),
                                             entitlementId,
                                             in.getEffectiveDate(),
@@ -418,19 +468,18 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                                             eventType,
                                             in.isBlockEntitlement(),
                                             in.isBlockBilling(),
-                                            in.getService(),
+                                            serviceName,
                                             in.getStateName(),
-                                            // We look for the next for the 'prev' meaning we we are headed to, but if this is null -- for example on cancellation we get the prev which gives the correct state.
-                                            prev != null ? (prev.getNextProduct() != null ? prev.getNextProduct() : prev.getPrevProduct()) : null,
-                                            prev != null ? (prev.getNextPlan() != null ? prev.getNextPlan() : prev.getPrevPlan()) : null,
-                                            prev != null ? (prev.getNextPhase() != null ? prev.getNextPhase() : prev.getPrevPhase()) : null,
-                                            prev != null ? (prev.getNextPriceList() != null ? prev.getNextPriceList() : prev.getPrevPriceList()) : null,
-                                            prev != null ? (prev.getNextBillingPeriod() != null ? prev.getNextBillingPeriod() : prev.getPrevBillingPeriod()) : null,
-                                            next != null ? next.getPrevProduct() : null,
-                                            next != null ? next.getPrevPlan() : null,
-                                            next != null ? next.getPrevPhase() : null,
-                                            next != null ? next.getPrevPriceList() : null,
-                                            next != null ? next.getPrevBillingPeriod() : null,
+                                            prevProduct,
+                                            prevPlan,
+                                            prevPlanPhase,
+                                            prevPriceList,
+                                            prevBillingPeriod,
+                                            nextProduct,
+                                            nextPlan,
+                                            nextPlanPhase,
+                                            nextPriceList,
+                                            nextBillingPeriod,
                                             in.getCreatedDate(),
                                             accountTimeZone);
     }
@@ -492,11 +541,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                 return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.STOP_BILLING);
             case PHASE:
                 return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.PHASE);
-            // This is the old way of pausing billing; not used any longer, but kept for compatibility reason. We return both RESUME_ENTITLEMENT and RESUME_BILLING
-            // and will rely on the sanitizeForBaseRecreateEvents method to transform the STOP_BILLING (coming from CANCEL) into the correct events.
-            //
-            case RE_CREATE:
-                return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.RESUME_ENTITLEMENT, SubscriptionEventType.RESUME_BILLING);
             /*
              * Those can be ignored:
              */
@@ -533,7 +577,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
     //
     // Internal class to keep the state associated with each subscription
     //
-    private final static class TargetState {
+    private static final class TargetState {
 
         private boolean isEntitlementStarted;
         private boolean isEntitlementStopped;
@@ -570,14 +614,12 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                                                                      e.getServiceStateName(), e.getServiceName(), false, e.isBlockedEntitlement(), e.isBlockedBilling(),
                                                                      ((DefaultSubscriptionEvent) e).getEffectiveDateTime());
             perServiceBlockingState.put(converted.getService(), converted);
-
         }
 
         //
         // From the current state of that subscription, compute the effect of the new state based on the incoming blockingState event
         //
         private List<SubscriptionEventType> addStateAndReturnEventTypes(final BlockingState bs) {
-
             // Turn off isBlockedEntitlement and isBlockedBilling if there was not start event
             final BlockingState fixedBlockingState = new DefaultBlockingState(bs.getBlockedId(),
                                                                               bs.getType(),
@@ -600,7 +642,15 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
             // across all services
             //
             final BlockingAggregator stateBefore = getState();
-            perServiceBlockingState.put(fixedBlockingState.getService(), fixedBlockingState);
+            if (DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME.equals(fixedBlockingState.getService())) {
+                // Some blocking states will be added as entitlement-service and billing-service via addEntitlementEvent
+                // (see above). Because of it, we need to multiplex entitlement events here.
+                // TODO - this is magic and fragile. We should revisit how we create this state machine.
+                perServiceBlockingState.put(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME, fixedBlockingState);
+                perServiceBlockingState.put(DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME, fixedBlockingState);
+            } else {
+                perServiceBlockingState.put(fixedBlockingState.getService(), fixedBlockingState);
+            }
             final BlockingAggregator stateAfter = getState();
 
             final boolean shouldResumeEntitlement = isEntitlementStarted && !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
@@ -629,314 +679,10 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
 
         private BlockingAggregator getState() {
             final DefaultBlockingAggregator aggrBefore = new DefaultBlockingAggregator();
-            for (BlockingState cur : perServiceBlockingState.values()) {
+            for (final BlockingState cur : perServiceBlockingState.values()) {
                 aggrBefore.or(cur);
             }
             return aggrBefore;
         }
     }
-
-    protected static final class DefaultSubscriptionEvent implements SubscriptionEvent {
-
-        private final UUID id;
-        private final UUID entitlementId;
-        private final DateTime effectiveDate;
-        private final DateTime requestedDate;
-        private final SubscriptionEventType eventType;
-        private final boolean isBlockingEntitlement;
-        private final boolean isBlockingBilling;
-        private final String serviceName;
-        private final String serviceStateName;
-        private final Product prevProduct;
-        private final Plan prevPlan;
-        private final PlanPhase prevPlanPhase;
-        private final PriceList prevPriceList;
-        private final BillingPeriod prevBillingPeriod;
-        private final Product nextProduct;
-        private final Plan nextPlan;
-        private final PlanPhase nextPlanPhase;
-        private final PriceList nextPriceList;
-        private final BillingPeriod nextBillingPeriod;
-        private final DateTime createdDate;
-        private final DateTimeZone accountTimeZone;
-
-        public DefaultSubscriptionEvent(final UUID id,
-                                        final UUID entitlementId,
-                                        final DateTime effectiveDate,
-                                        final DateTime requestedDate,
-                                        final SubscriptionEventType eventType,
-                                        final boolean blockingEntitlement,
-                                        final boolean blockingBilling,
-                                        final String serviceName,
-                                        final String serviceStateName,
-                                        final Product prevProduct,
-                                        final Plan prevPlan,
-                                        final PlanPhase prevPlanPhase,
-                                        final PriceList prevPriceList,
-                                        final BillingPeriod prevBillingPeriod,
-                                        final Product nextProduct,
-                                        final Plan nextPlan,
-                                        final PlanPhase nextPlanPhase,
-                                        final PriceList nextPriceList,
-                                        final BillingPeriod nextBillingPeriod,
-                                        final DateTime createDate,
-                                        final DateTimeZone accountTimeZone) {
-            this.id = id;
-            this.entitlementId = entitlementId;
-            this.effectiveDate = effectiveDate;
-            this.requestedDate = requestedDate;
-            this.eventType = eventType;
-            this.isBlockingEntitlement = blockingEntitlement;
-            this.isBlockingBilling = blockingBilling;
-            this.serviceName = serviceName;
-            this.serviceStateName = serviceStateName;
-            this.prevProduct = prevProduct;
-            this.prevPlan = prevPlan;
-            this.prevPlanPhase = prevPlanPhase;
-            this.prevPriceList = prevPriceList;
-            this.prevBillingPeriod = prevBillingPeriod;
-            this.nextProduct = nextProduct;
-            this.nextPlan = nextPlan;
-            this.nextPlanPhase = nextPlanPhase;
-            this.nextPriceList = nextPriceList;
-            this.nextBillingPeriod = nextBillingPeriod;
-            this.createdDate = createDate;
-            this.accountTimeZone = accountTimeZone;
-        }
-
-        private DefaultSubscriptionEvent(DefaultSubscriptionEvent copy, SubscriptionEventType newEventType) {
-            this(copy.getId(),
-                 copy.getEntitlementId(),
-                 copy.getEffectiveDateTime(),
-                 copy.getRequestedDateTime(),
-                 newEventType,
-                 copy.isBlockedEntitlement(),
-                 copy.isBlockedBilling(),
-                 copy.getServiceName(),
-                 copy.getServiceStateName(),
-                 copy.getPrevProduct(),
-                 copy.getPrevPlan(),
-                 copy.getPrevPhase(),
-                 copy.getPrevPriceList(),
-                 copy.getPrevBillingPeriod(),
-                 copy.getNextProduct(),
-                 copy.getNextPlan(),
-                 copy.getNextPhase(),
-                 copy.getNextPriceList(),
-                 copy.getNextBillingPeriod(),
-                 copy.getCreatedDate(),
-                 copy.getAccountTimeZone());
-        }
-
-        public DateTimeZone getAccountTimeZone() {
-            return accountTimeZone;
-        }
-
-        public DateTime getEffectiveDateTime() {
-            return effectiveDate;
-        }
-
-        public DateTime getRequestedDateTime() {
-            return requestedDate;
-        }
-
-        @Override
-        public UUID getId() {
-            return id;
-        }
-
-        @Override
-        public UUID getEntitlementId() {
-            return entitlementId;
-        }
-
-        @Override
-        public LocalDate getEffectiveDate() {
-            return effectiveDate != null ? new LocalDate(effectiveDate, accountTimeZone) : null;
-        }
-
-        @Override
-        public LocalDate getRequestedDate() {
-            return requestedDate != null ? new LocalDate(requestedDate, accountTimeZone) : null;
-        }
-
-        @Override
-        public SubscriptionEventType getSubscriptionEventType() {
-            return eventType;
-        }
-
-        @Override
-        public boolean isBlockedBilling() {
-            return isBlockingBilling;
-        }
-
-        @Override
-        public boolean isBlockedEntitlement() {
-            return isBlockingEntitlement;
-        }
-
-        @Override
-        public String getServiceName() {
-            return serviceName;
-        }
-
-        @Override
-        public String getServiceStateName() {
-            return serviceStateName;
-        }
-
-        @Override
-        public Product getPrevProduct() {
-            return prevProduct;
-        }
-
-        @Override
-        public Plan getPrevPlan() {
-            return prevPlan;
-        }
-
-        @Override
-        public PlanPhase getPrevPhase() {
-            return prevPlanPhase;
-        }
-
-        @Override
-        public PriceList getPrevPriceList() {
-            return prevPriceList;
-        }
-
-        @Override
-        public BillingPeriod getPrevBillingPeriod() {
-            return prevBillingPeriod;
-        }
-
-        @Override
-        public Product getNextProduct() {
-            return nextProduct;
-        }
-
-        @Override
-        public Plan getNextPlan() {
-            return nextPlan;
-        }
-
-        @Override
-        public PlanPhase getNextPhase() {
-            return nextPlanPhase;
-        }
-
-        @Override
-        public PriceList getNextPriceList() {
-            return nextPriceList;
-        }
-
-        @Override
-        public BillingPeriod getNextBillingPeriod() {
-            return nextBillingPeriod;
-        }
-
-        public DateTime getCreatedDate() {
-            return createdDate;
-        }
-
-        @Override
-        public boolean equals(final Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-
-            final DefaultSubscriptionEvent that = (DefaultSubscriptionEvent) o;
-
-            if (isBlockingBilling != that.isBlockingBilling) {
-                return false;
-            }
-            if (isBlockingEntitlement != that.isBlockingEntitlement) {
-                return false;
-            }
-            if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
-                return false;
-            }
-            if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
-                return false;
-            }
-            if (entitlementId != null ? !entitlementId.equals(that.entitlementId) : that.entitlementId != null) {
-                return false;
-            }
-            if (eventType != that.eventType) {
-                return false;
-            }
-            if (id != null ? !id.equals(that.id) : that.id != null) {
-                return false;
-            }
-            if (nextBillingPeriod != that.nextBillingPeriod) {
-                return false;
-            }
-            if (nextPlan != null ? !nextPlan.equals(that.nextPlan) : that.nextPlan != null) {
-                return false;
-            }
-            if (nextPlanPhase != null ? !nextPlanPhase.equals(that.nextPlanPhase) : that.nextPlanPhase != null) {
-                return false;
-            }
-            if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
-                return false;
-            }
-            if (nextProduct != null ? !nextProduct.equals(that.nextProduct) : that.nextProduct != null) {
-                return false;
-            }
-            if (prevBillingPeriod != that.prevBillingPeriod) {
-                return false;
-            }
-            if (prevPlan != null ? !prevPlan.equals(that.prevPlan) : that.prevPlan != null) {
-                return false;
-            }
-            if (prevPlanPhase != null ? !prevPlanPhase.equals(that.prevPlanPhase) : that.prevPlanPhase != null) {
-                return false;
-            }
-            if (prevPriceList != null ? !prevPriceList.equals(that.prevPriceList) : that.prevPriceList != null) {
-                return false;
-            }
-            if (prevProduct != null ? !prevProduct.equals(that.prevProduct) : that.prevProduct != null) {
-                return false;
-            }
-            if (requestedDate != null ? !requestedDate.equals(that.requestedDate) : that.requestedDate != null) {
-                return false;
-            }
-            if (serviceName != null ? !serviceName.equals(that.serviceName) : that.serviceName != null) {
-                return false;
-            }
-            if (serviceStateName != null ? !serviceStateName.equals(that.serviceStateName) : that.serviceStateName != null) {
-                return false;
-            }
-
-            return true;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = id != null ? id.hashCode() : 0;
-            result = 31 * result + (entitlementId != null ? entitlementId.hashCode() : 0);
-            result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
-            result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
-            result = 31 * result + (eventType != null ? eventType.hashCode() : 0);
-            result = 31 * result + (isBlockingEntitlement ? 1 : 0);
-            result = 31 * result + (isBlockingBilling ? 1 : 0);
-            result = 31 * result + (serviceName != null ? serviceName.hashCode() : 0);
-            result = 31 * result + (serviceStateName != null ? serviceStateName.hashCode() : 0);
-            result = 31 * result + (prevProduct != null ? prevProduct.hashCode() : 0);
-            result = 31 * result + (prevPlan != null ? prevPlan.hashCode() : 0);
-            result = 31 * result + (prevPlanPhase != null ? prevPlanPhase.hashCode() : 0);
-            result = 31 * result + (prevPriceList != null ? prevPriceList.hashCode() : 0);
-            result = 31 * result + (prevBillingPeriod != null ? prevBillingPeriod.hashCode() : 0);
-            result = 31 * result + (nextProduct != null ? nextProduct.hashCode() : 0);
-            result = 31 * result + (nextPlan != null ? nextPlan.hashCode() : 0);
-            result = 31 * result + (nextPlanPhase != null ? nextPlanPhase.hashCode() : 0);
-            result = 31 * result + (nextPriceList != null ? nextPriceList.hashCode() : 0);
-            result = 31 * result + (nextBillingPeriod != null ? nextBillingPeriod.hashCode() : 0);
-            result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
-            return result;
-        }
-    }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionEvent.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionEvent.java
new file mode 100644
index 0000000..fbd7ab6
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionEvent.java
@@ -0,0 +1,396 @@
+/*
+ * 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.entitlement.api;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PriceList;
+import com.ning.billing.catalog.api.Product;
+
+public class DefaultSubscriptionEvent implements SubscriptionEvent {
+
+    private final UUID id;
+    private final UUID entitlementId;
+    private final DateTime effectiveDate;
+    private final DateTime requestedDate;
+    private final SubscriptionEventType eventType;
+    private final boolean isBlockingEntitlement;
+    private final boolean isBlockingBilling;
+    private final String serviceName;
+    private final String serviceStateName;
+    private final Product prevProduct;
+    private final Plan prevPlan;
+    private final PlanPhase prevPlanPhase;
+    private final PriceList prevPriceList;
+    private final BillingPeriod prevBillingPeriod;
+    private final Product nextProduct;
+    private final Plan nextPlan;
+    private final PlanPhase nextPlanPhase;
+    private final PriceList nextPriceList;
+    private final BillingPeriod nextBillingPeriod;
+    private final DateTime createdDate;
+    private final DateTimeZone accountTimeZone;
+
+    public DefaultSubscriptionEvent(final UUID id,
+                                    final UUID entitlementId,
+                                    final DateTime effectiveDate,
+                                    final DateTime requestedDate,
+                                    final SubscriptionEventType eventType,
+                                    final boolean blockingEntitlement,
+                                    final boolean blockingBilling,
+                                    final String serviceName,
+                                    final String serviceStateName,
+                                    final Product prevProduct,
+                                    final Plan prevPlan,
+                                    final PlanPhase prevPlanPhase,
+                                    final PriceList prevPriceList,
+                                    final BillingPeriod prevBillingPeriod,
+                                    final Product nextProduct,
+                                    final Plan nextPlan,
+                                    final PlanPhase nextPlanPhase,
+                                    final PriceList nextPriceList,
+                                    final BillingPeriod nextBillingPeriod,
+                                    final DateTime createDate,
+                                    final DateTimeZone accountTimeZone) {
+        this.id = id;
+        this.entitlementId = entitlementId;
+        this.effectiveDate = effectiveDate;
+        this.requestedDate = requestedDate;
+        this.eventType = eventType;
+        this.isBlockingEntitlement = blockingEntitlement;
+        this.isBlockingBilling = blockingBilling;
+        this.serviceName = serviceName;
+        this.serviceStateName = serviceStateName;
+        this.prevProduct = prevProduct;
+        this.prevPlan = prevPlan;
+        this.prevPlanPhase = prevPlanPhase;
+        this.prevPriceList = prevPriceList;
+        this.prevBillingPeriod = prevBillingPeriod;
+        this.nextProduct = nextProduct;
+        this.nextPlan = nextPlan;
+        this.nextPlanPhase = nextPlanPhase;
+        this.nextPriceList = nextPriceList;
+        this.nextBillingPeriod = nextBillingPeriod;
+        this.createdDate = createDate;
+        this.accountTimeZone = accountTimeZone;
+    }
+
+    public DefaultSubscriptionEvent(final DefaultSubscriptionEvent copy, final SubscriptionEventType newEventType) {
+        this(copy.getId(),
+             copy.getEntitlementId(),
+             copy.getEffectiveDateTime(),
+             copy.getRequestedDateTime(),
+             newEventType,
+             copy.isBlockedEntitlement(),
+             copy.isBlockedBilling(),
+             copy.getServiceName(),
+             copy.getServiceStateName(),
+             copy.getPrevProduct(),
+             copy.getPrevPlan(),
+             copy.getPrevPhase(),
+             copy.getPrevPriceList(),
+             copy.getPrevBillingPeriod(),
+             copy.getNextProduct(),
+             copy.getNextPlan(),
+             copy.getNextPhase(),
+             copy.getNextPriceList(),
+             copy.getNextBillingPeriod(),
+             copy.getCreatedDate(),
+             copy.getAccountTimeZone());
+    }
+
+    public DateTimeZone getAccountTimeZone() {
+        return accountTimeZone;
+    }
+
+    public DateTime getEffectiveDateTime() {
+        return effectiveDate;
+    }
+
+    public DateTime getRequestedDateTime() {
+        return requestedDate;
+    }
+
+    @Override
+    public UUID getId() {
+        return id;
+    }
+
+    @Override
+    public UUID getEntitlementId() {
+        return entitlementId;
+    }
+
+    @Override
+    public LocalDate getEffectiveDate() {
+        return effectiveDate != null ? new LocalDate(effectiveDate, accountTimeZone) : null;
+    }
+
+    @Override
+    public LocalDate getRequestedDate() {
+        return requestedDate != null ? new LocalDate(requestedDate, accountTimeZone) : null;
+    }
+
+    @Override
+    public SubscriptionEventType getSubscriptionEventType() {
+        return eventType;
+    }
+
+    @Override
+    public boolean isBlockedBilling() {
+        return isBlockingBilling;
+    }
+
+    @Override
+    public boolean isBlockedEntitlement() {
+        return isBlockingEntitlement;
+    }
+
+    @Override
+    public String getServiceName() {
+        return serviceName;
+    }
+
+    @Override
+    public String getServiceStateName() {
+        return serviceStateName;
+    }
+
+    @Override
+    public Product getPrevProduct() {
+        return prevProduct;
+    }
+
+    @Override
+    public Plan getPrevPlan() {
+        return prevPlan;
+    }
+
+    @Override
+    public PlanPhase getPrevPhase() {
+        return prevPlanPhase;
+    }
+
+    @Override
+    public PriceList getPrevPriceList() {
+        return prevPriceList;
+    }
+
+    @Override
+    public BillingPeriod getPrevBillingPeriod() {
+        return prevBillingPeriod;
+    }
+
+    @Override
+    public Product getNextProduct() {
+        return nextProduct;
+    }
+
+    @Override
+    public Plan getNextPlan() {
+        return nextPlan;
+    }
+
+    @Override
+    public PlanPhase getNextPhase() {
+        return nextPlanPhase;
+    }
+
+    @Override
+    public PriceList getNextPriceList() {
+        return nextPriceList;
+    }
+
+    @Override
+    public BillingPeriod getNextBillingPeriod() {
+        return nextBillingPeriod;
+    }
+
+    public DateTime getCreatedDate() {
+        return createdDate;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final DefaultSubscriptionEvent that = (DefaultSubscriptionEvent) o;
+
+        if (isBlockingBilling != that.isBlockingBilling) {
+            return false;
+        }
+        if (isBlockingEntitlement != that.isBlockingEntitlement) {
+            return false;
+        }
+        if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
+            return false;
+        }
+        if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
+            return false;
+        }
+        if (entitlementId != null ? !entitlementId.equals(that.entitlementId) : that.entitlementId != null) {
+            return false;
+        }
+        if (eventType != that.eventType) {
+            return false;
+        }
+        if (id != null ? !id.equals(that.id) : that.id != null) {
+            return false;
+        }
+        if (nextBillingPeriod != that.nextBillingPeriod) {
+            return false;
+        }
+        if (nextPlan != null ? !nextPlan.equals(that.nextPlan) : that.nextPlan != null) {
+            return false;
+        }
+        if (nextPlanPhase != null ? !nextPlanPhase.equals(that.nextPlanPhase) : that.nextPlanPhase != null) {
+            return false;
+        }
+        if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
+            return false;
+        }
+        if (nextProduct != null ? !nextProduct.equals(that.nextProduct) : that.nextProduct != null) {
+            return false;
+        }
+        if (prevBillingPeriod != that.prevBillingPeriod) {
+            return false;
+        }
+        if (prevPlan != null ? !prevPlan.equals(that.prevPlan) : that.prevPlan != null) {
+            return false;
+        }
+        if (prevPlanPhase != null ? !prevPlanPhase.equals(that.prevPlanPhase) : that.prevPlanPhase != null) {
+            return false;
+        }
+        if (prevPriceList != null ? !prevPriceList.equals(that.prevPriceList) : that.prevPriceList != null) {
+            return false;
+        }
+        if (prevProduct != null ? !prevProduct.equals(that.prevProduct) : that.prevProduct != null) {
+            return false;
+        }
+        if (requestedDate != null ? !requestedDate.equals(that.requestedDate) : that.requestedDate != null) {
+            return false;
+        }
+        if (serviceName != null ? !serviceName.equals(that.serviceName) : that.serviceName != null) {
+            return false;
+        }
+        if (serviceStateName != null ? !serviceStateName.equals(that.serviceStateName) : that.serviceStateName != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public boolean overlaps(final DefaultSubscriptionEvent that) {
+        if (this == that) {
+            return true;
+        }
+        if (that == null || getClass() != that.getClass()) {
+            return false;
+        }
+
+        if (isBlockingBilling != that.isBlockingBilling) {
+            return false;
+        }
+        if (isBlockingEntitlement != that.isBlockingEntitlement) {
+            return false;
+        }
+        if (effectiveDate != null ? effectiveDate.compareTo(that.effectiveDate) < 0 : that.effectiveDate != null) {
+            return false;
+        }
+        if (entitlementId != null ? !entitlementId.equals(that.entitlementId) : that.entitlementId != null) {
+            return false;
+        }
+        if (eventType != that.eventType) {
+            return false;
+        }
+        if (nextBillingPeriod != that.nextBillingPeriod) {
+            return false;
+        }
+        if (nextPlan != null ? !nextPlan.equals(that.nextPlan) : that.nextPlan != null) {
+            return false;
+        }
+        if (nextPlanPhase != null ? !nextPlanPhase.equals(that.nextPlanPhase) : that.nextPlanPhase != null) {
+            return false;
+        }
+        if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
+            return false;
+        }
+        if (nextProduct != null ? !nextProduct.equals(that.nextProduct) : that.nextProduct != null) {
+            return false;
+        }
+        if (prevBillingPeriod != that.prevBillingPeriod) {
+            return false;
+        }
+        if (prevPlan != null ? !prevPlan.equals(that.prevPlan) : that.prevPlan != null) {
+            return false;
+        }
+        if (prevPlanPhase != null ? !prevPlanPhase.equals(that.prevPlanPhase) : that.prevPlanPhase != null) {
+            return false;
+        }
+        if (prevPriceList != null ? !prevPriceList.equals(that.prevPriceList) : that.prevPriceList != null) {
+            return false;
+        }
+        if (prevProduct != null ? !prevProduct.equals(that.prevProduct) : that.prevProduct != null) {
+            return false;
+        }
+        if (serviceName != null ? !serviceName.equals(that.serviceName) : that.serviceName != null) {
+            return false;
+        }
+        if (serviceStateName != null ? !serviceStateName.equals(that.serviceStateName) : that.serviceStateName != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = id != null ? id.hashCode() : 0;
+        result = 31 * result + (entitlementId != null ? entitlementId.hashCode() : 0);
+        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+        result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
+        result = 31 * result + (eventType != null ? eventType.hashCode() : 0);
+        result = 31 * result + (isBlockingEntitlement ? 1 : 0);
+        result = 31 * result + (isBlockingBilling ? 1 : 0);
+        result = 31 * result + (serviceName != null ? serviceName.hashCode() : 0);
+        result = 31 * result + (serviceStateName != null ? serviceStateName.hashCode() : 0);
+        result = 31 * result + (prevProduct != null ? prevProduct.hashCode() : 0);
+        result = 31 * result + (prevPlan != null ? prevPlan.hashCode() : 0);
+        result = 31 * result + (prevPlanPhase != null ? prevPlanPhase.hashCode() : 0);
+        result = 31 * result + (prevPriceList != null ? prevPriceList.hashCode() : 0);
+        result = 31 * result + (prevBillingPeriod != null ? prevBillingPeriod.hashCode() : 0);
+        result = 31 * result + (nextProduct != null ? nextProduct.hashCode() : 0);
+        result = 31 * result + (nextPlan != null ? nextPlan.hashCode() : 0);
+        result = 31 * result + (nextPlanPhase != null ? nextPlanPhase.hashCode() : 0);
+        result = 31 * result + (nextPriceList != null ? nextPriceList.hashCode() : 0);
+        result = 31 * result + (nextBillingPeriod != null ? nextBillingPeriod.hashCode() : 0);
+        result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
+        return result;
+    }
+}
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 6f27ece..bcfd0f1 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
@@ -32,6 +32,9 @@ import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
 import com.ning.billing.junction.DefaultBlockingState;
+import com.ning.billing.util.api.AuditLevel;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.audit.ChangeType;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNull;
@@ -175,4 +178,56 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
         assertNull(thirdBundle.getSubscriptions().get(0).getBillingEndDate());
         assertEquals(thirdBundle.getOriginalCreatedDate().compareTo(firstbundle.getCreatedDate()), 0);
     }
+
+    @Test(groups = "slow", description = "Test for https://github.com/killbill/killbill/issues/136")
+    public void testAuditLogsForEntitlementAndSubscriptionBaseObjects() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+
+        // Create entitlement
+        testListener.pushExpectedEvent(NextEvent.CREATE);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), initialDate, callContext);
+        assertListenerStatus();
+
+        // Get the phase event out of the way
+        testListener.pushExpectedEvents(NextEvent.PHASE);
+        clock.setDay(new LocalDate(2013, 9, 7));
+        assertListenerStatus();
+
+        final LocalDate pauseDate = new LocalDate(2013, 9, 17);
+        entitlementApi.pause(baseEntitlement.getBundleId(), pauseDate, callContext);
+
+        final LocalDate resumeDate = new LocalDate(2013, 12, 24);
+        entitlementApi.resume(baseEntitlement.getBundleId(), resumeDate, callContext);
+
+        final LocalDate cancelDate = new LocalDate(2013, 12, 27);
+        baseEntitlement.cancelEntitlementWithDate(cancelDate, true, callContext);
+
+        testListener.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK, NextEvent.RESUME, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.BLOCK);
+        clock.setDay(cancelDate.plusDays(1));
+        assertListenerStatus();
+
+        final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
+        final List<SubscriptionEvent> transitions = bundle.getTimeline().getSubscriptionEvents();
+        assertEquals(transitions.size(), 9);
+        checkSubscriptionEventAuditLog(transitions, 0, SubscriptionEventType.START_ENTITLEMENT);
+        checkSubscriptionEventAuditLog(transitions, 1, SubscriptionEventType.START_BILLING);
+        checkSubscriptionEventAuditLog(transitions, 2, SubscriptionEventType.PHASE);
+        checkSubscriptionEventAuditLog(transitions, 3, SubscriptionEventType.PAUSE_ENTITLEMENT);
+        checkSubscriptionEventAuditLog(transitions, 4, SubscriptionEventType.PAUSE_BILLING);
+        checkSubscriptionEventAuditLog(transitions, 5, SubscriptionEventType.RESUME_ENTITLEMENT);
+        checkSubscriptionEventAuditLog(transitions, 6, SubscriptionEventType.RESUME_BILLING);
+        checkSubscriptionEventAuditLog(transitions, 7, SubscriptionEventType.STOP_ENTITLEMENT);
+        checkSubscriptionEventAuditLog(transitions, 8, SubscriptionEventType.STOP_BILLING);
+    }
+
+    private void checkSubscriptionEventAuditLog(final List<SubscriptionEvent> transitions, final int idx, final SubscriptionEventType expectedType) {
+        assertEquals(transitions.get(idx).getSubscriptionEventType(), expectedType);
+        final List<AuditLog> auditLogs = auditUserApi.getAuditLogs(transitions.get(idx).getId(), transitions.get(idx).getSubscriptionEventType().getObjectType(), AuditLevel.FULL, callContext);
+        assertEquals(auditLogs.size(), 1);
+        assertEquals(auditLogs.get(0).getChangeType(), ChangeType.INSERT);
+    }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
index 359f73c..fd225ad 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
@@ -29,7 +29,6 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
@@ -45,6 +44,7 @@ import com.ning.billing.subscription.events.SubscriptionBaseEvent.EventType;
 import com.ning.billing.subscription.events.user.ApiEventType;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
 
 public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteNoDB {
 
@@ -56,7 +56,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         bundleId = UUID.randomUUID();
     }
 
-
     public class TestSubscriptionBundleTimeline extends DefaultSubscriptionBundleTimeline {
 
         public TestSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final List<Entitlement> entitlements, final List<BlockingState> allBlockingStates) {
@@ -65,33 +64,33 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
         public SubscriptionEvent createEvent(final UUID subscriptionId, final SubscriptionEventType type, final DateTime effectiveDate) {
             return new DefaultSubscriptionEvent(UUID.randomUUID(),
-                                         subscriptionId,
-                                         effectiveDate,
-                                         null,
-                                         type,
-                                         true,
-                                         true,
-                                         "foo",
-                                         "bar",
-                                         null,
-                                         null,
-                                         null,
-                                         null,
-                                         null,
-                                         null,
-                                         null,
-                                         null,
-                                         null,
-                                         null,
-                                         null,
-                                         null);
+                                                subscriptionId,
+                                                effectiveDate,
+                                                null,
+                                                type,
+                                                true,
+                                                true,
+                                                "foo",
+                                                "bar",
+                                                null,
+                                                null,
+                                                null,
+                                                null,
+                                                null,
+                                                null,
+                                                null,
+                                                null,
+                                                null,
+                                                null,
+                                                null,
+                                                null);
 
         }
     }
 
     @Test(groups = "fast")
     public void testReOrderSubscriptionEventsOnInvalidOrder1() {
-        TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>(), new ArrayList<BlockingState>());
+        final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>(), new ArrayList<BlockingState>());
 
         final List<SubscriptionEvent> events = new ArrayList<SubscriptionEvent>();
         final UUID subscriptionId = UUID.randomUUID();
@@ -109,10 +108,9 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         Assert.assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
     }
 
-
     @Test(groups = "fast")
     public void testReOrderSubscriptionEventsOnInvalidOrder2() {
-        TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>(), new ArrayList<BlockingState>());
+        final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>(), new ArrayList<BlockingState>());
 
         final List<SubscriptionEvent> events = new ArrayList<SubscriptionEvent>();
         final UUID subscriptionId = UUID.randomUUID();
@@ -132,7 +130,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast")
     public void testReOrderSubscriptionEventsOnInvalidOrder3() {
-        TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>(), new ArrayList<BlockingState>());
+        final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>(), new ArrayList<BlockingState>());
 
         final List<SubscriptionEvent> events = new ArrayList<SubscriptionEvent>();
         final UUID subscriptionId = UUID.randomUUID();
@@ -150,10 +148,9 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         Assert.assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
     }
 
-
     @Test(groups = "fast")
     public void testReOrderSubscriptionEventsOnInvalidOrderAndDifferentSubscriptionsSameDates1() {
-        TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>(), new ArrayList<BlockingState>());
+        final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>(), new ArrayList<BlockingState>());
 
         final List<SubscriptionEvent> events = new ArrayList<SubscriptionEvent>();
         final UUID subscriptionId = UUID.fromString("60b64e0c-cefd-48c3-8de9-c731a9558165");
@@ -180,7 +177,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast")
     public void testReOrderSubscriptionEventsOnInvalidOrderAndDifferentSubscriptionsSameDates2() {
-        TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>(), new ArrayList<BlockingState>());
+        final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>(), new ArrayList<BlockingState>());
 
         final List<SubscriptionEvent> events = new ArrayList<SubscriptionEvent>();
         final UUID subscriptionId = UUID.fromString("35b3b340-31b2-46ea-b062-e9fc9fab3bc9");
@@ -208,7 +205,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast")
     public void testReOrderSubscriptionEventsOnInvalidOrderAndDifferentSubscriptionsDates() {
-        TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>(), new ArrayList<BlockingState>());
+        final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>(), new ArrayList<BlockingState>());
 
         final List<SubscriptionEvent> events = new ArrayList<SubscriptionEvent>();
         final UUID subscriptionId = UUID.randomUUID();
@@ -242,13 +239,11 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         Assert.assertEquals(events.get(7).getSubscriptionEventType(), SubscriptionEventType.PAUSE_BILLING);
         Assert.assertEquals(events.get(8).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
         Assert.assertEquals(events.get(9).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
-
-
     }
 
     @Test(groups = "fast")
     public void testReOrderSubscriptionEventsOnCorrectOrder() {
-        TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>(), new ArrayList<BlockingState>());
+        final TestSubscriptionBundleTimeline timeline = new TestSubscriptionBundleTimeline(null, null, null, null, new ArrayList<Entitlement>(), new ArrayList<BlockingState>());
 
         final List<SubscriptionEvent> events = new ArrayList<SubscriptionEvent>();
         final UUID subscriptionId = UUID.randomUUID();
@@ -268,7 +263,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast")
     public void testOneEntitlementNoBlockingStates() throws CatalogApiException {
-
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -276,7 +270,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final UUID bundleId = UUID.randomUUID();
         final String externalKey = "foo";
 
-
         final UUID entitlementId = UUID.randomUUID();
 
         final List<SubscriptionBaseTransition> allTransitions = new ArrayList<SubscriptionBaseTransition>();
@@ -291,24 +284,22 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
         allTransitions.add(tr2);
 
-
         effectiveDate = effectiveDate.plusDays(15);
         clock.addDays(15);
         final SubscriptionBaseTransition tr3 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CANCEL, requestedDate, effectiveDate, clock.getUTCNow(), "phase", null);
         allTransitions.add(tr3);
 
-
         final List<Entitlement> entitlements = new ArrayList<Entitlement>();
         final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
         entitlements.add(entitlement);
 
-        final DefaultSubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, Collections.<BlockingState>emptyList());
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, Collections.<BlockingState>emptyList());
 
         assertEquals(timeline.getAccountId(), accountId);
         assertEquals(timeline.getBundleId(), bundleId);
         assertEquals(timeline.getExternalKey(), externalKey);
 
-        List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
+        final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
         assertEquals(events.size(), 4);
 
         assertEquals(events.get(0).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
@@ -321,14 +312,23 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(2).getSubscriptionEventType(), SubscriptionEventType.PHASE);
         assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
 
+        assertEquals(events.get(0).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(1).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+        assertEquals(events.get(2).getServiceName(), DefaultSubscriptionBundleTimeline.ENT_BILLING_SERVICE_NAME);
+        assertEquals(events.get(3).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+
+        assertNull(events.get(0).getPrevPhase());
         assertEquals(events.get(0).getNextPhase().getName(), "trial");
+        assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial");
+        assertEquals(events.get(2).getPrevPhase().getName(), "trial");
         assertEquals(events.get(2).getNextPhase().getName(), "phase");
-        assertEquals(events.get(3).getNextPhase(), null);
+        assertEquals(events.get(3).getPrevPhase().getName(), "phase");
+        assertNull(events.get(3).getNextPhase());
     }
 
-    @Test(groups = "fast")
-    public void testOneEntitlementWithRecreate() throws CatalogApiException {
+    @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/135")
+    public void testOneEntitlementWithPauseResume() throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -339,6 +339,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final UUID entitlementId = UUID.randomUUID();
 
         final List<SubscriptionBaseTransition> allTransitions = new ArrayList<SubscriptionBaseTransition>();
+        final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
 
         final DateTime requestedDate = new DateTime();
         DateTime effectiveDate = new DateTime(2013, 1, 1, 15, 43, 25, 0, DateTimeZone.UTC);
@@ -350,57 +351,112 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
         allTransitions.add(tr2);
 
+        effectiveDate = effectiveDate.plusDays(12);
+        clock.addDays(12);
+        final BlockingState bs1 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                           "NothingUseful1", DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                           true, true, true, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+        blockingStates.add(bs1);
+
+        effectiveDate = effectiveDate.plusDays(42);
+        clock.addDays(42);
+        final BlockingState bs2 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                           "NothingUseful2", DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                           false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+        blockingStates.add(bs2);
+
         effectiveDate = effectiveDate.plusDays(15);
         clock.addDays(15);
-        final SubscriptionBaseTransition tr3 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CANCEL, requestedDate, effectiveDate, clock.getUTCNow(), "phase", null);
-        allTransitions.add(tr3);
+        final String service = "boo";
+        final BlockingState bs3 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                           "NothingUseful3", service,
+                                                           false, false, true, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+        blockingStates.add(bs3);
 
         effectiveDate = effectiveDate.plusDays(15);
         clock.addDays(15);
-        final SubscriptionBaseTransition tr4 = createTransition(entitlementId, EventType.API_USER, ApiEventType.RE_CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "phase");
-        allTransitions.add(tr4);
+        final BlockingState bs4 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                           "NothingUseful4", service,
+                                                           false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+        blockingStates.add(bs4);
 
         final List<Entitlement> entitlements = new ArrayList<Entitlement>();
         final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
         entitlements.add(entitlement);
 
-        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, Collections.<BlockingState>emptyList());
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, blockingStates);
 
         assertEquals(timeline.getAccountId(), accountId);
         assertEquals(timeline.getBundleId(), bundleId);
         assertEquals(timeline.getExternalKey(), externalKey);
 
         final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
-        assertEquals(events.size(), 7);
+        assertEquals(events.size(), 9);
 
         assertEquals(events.get(0).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
         assertEquals(events.get(1).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
         assertEquals(events.get(2).getEffectiveDate().compareTo(new LocalDate(tr2.getEffectiveTransitionTime(), accountTimeZone)), 0);
-        assertEquals(events.get(3).getEffectiveDate().compareTo(new LocalDate(tr3.getEffectiveTransitionTime(), accountTimeZone)), 0);
-        assertEquals(events.get(4).getEffectiveDate().compareTo(new LocalDate(tr3.getEffectiveTransitionTime(), accountTimeZone)), 0);
-        assertEquals(events.get(5).getEffectiveDate().compareTo(new LocalDate(tr4.getEffectiveTransitionTime(), accountTimeZone)), 0);
-        assertEquals(events.get(6).getEffectiveDate().compareTo(new LocalDate(tr4.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(3).getEffectiveDate().compareTo(new LocalDate(bs1.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(4).getEffectiveDate().compareTo(new LocalDate(bs1.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(5).getEffectiveDate().compareTo(new LocalDate(bs2.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(6).getEffectiveDate().compareTo(new LocalDate(bs2.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(7).getEffectiveDate().compareTo(new LocalDate(bs3.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(8).getEffectiveDate().compareTo(new LocalDate(bs4.getEffectiveDate(), accountTimeZone)), 0);
 
         assertEquals(events.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
         assertEquals(events.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
+
         assertEquals(events.get(2).getSubscriptionEventType(), SubscriptionEventType.PHASE);
+
         assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.PAUSE_ENTITLEMENT);
         assertEquals(events.get(4).getSubscriptionEventType(), SubscriptionEventType.PAUSE_BILLING);
         assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.RESUME_ENTITLEMENT);
         assertEquals(events.get(6).getSubscriptionEventType(), SubscriptionEventType.RESUME_BILLING);
 
+        assertEquals(events.get(7).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
+        assertEquals(events.get(8).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
+
+        assertEquals(events.get(0).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(1).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+
+        assertEquals(events.get(2).getServiceName(), DefaultSubscriptionBundleTimeline.ENT_BILLING_SERVICE_NAME);
+
+        assertEquals(events.get(3).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(4).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+        assertEquals(events.get(5).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(6).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+
+        assertEquals(events.get(7).getServiceName(), service);
+        assertEquals(events.get(8).getServiceName(), service);
+
+        assertNull(events.get(0).getPrevPhase());
         assertEquals(events.get(0).getNextPhase().getName(), "trial");
+        assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial");
+
+        assertEquals(events.get(2).getPrevPhase().getName(), "trial");
         assertEquals(events.get(2).getNextPhase().getName(), "phase");
-        assertEquals(events.get(3).getNextPhase(), null);
-        assertEquals(events.get(4).getNextPhase(), null);
+
+        assertEquals(events.get(3).getPrevPhase().getName(), "phase");
+        assertEquals(events.get(3).getNextPhase().getName(), "phase");
+        assertEquals(events.get(4).getPrevPhase().getName(), "phase");
+        assertEquals(events.get(4).getNextPhase().getName(), "phase");
+        assertEquals(events.get(5).getPrevPhase().getName(), "phase");
         assertEquals(events.get(5).getNextPhase().getName(), "phase");
+        assertEquals(events.get(6).getPrevPhase().getName(), "phase");
         assertEquals(events.get(6).getNextPhase().getName(), "phase");
+        assertEquals(events.get(7).getPrevPhase().getName(), "phase");
+        assertEquals(events.get(7).getNextPhase().getName(), "phase");
+        assertEquals(events.get(8).getPrevPhase().getName(), "phase");
+        assertEquals(events.get(8).getNextPhase().getName(), "phase");
     }
 
     @Test(groups = "fast")
     public void testOneEntitlementWithInitialBlockingState() throws CatalogApiException {
-
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -408,7 +464,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final UUID bundleId = UUID.randomUUID();
         final String externalKey = "foo";
 
-
         final UUID entitlementId = UUID.randomUUID();
 
         final List<SubscriptionBaseTransition> allTransitions = new ArrayList<SubscriptionBaseTransition>();
@@ -431,31 +486,29 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
         allTransitions.add(tr2);
 
-
+        final String service = "boo";
         final BlockingState bs2 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
-                                                           "NothingUseful", "boo",
+                                                           "NothingUseful", service,
                                                            false, false, false, clock.getUTCNow(), clock.getUTCNow(), clock.getUTCNow());
 
         blockingStates.add(bs2);
 
-
         effectiveDate = effectiveDate.plusDays(15);
         clock.addDays(15);
         final SubscriptionBaseTransition tr3 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CANCEL, requestedDate, effectiveDate, clock.getUTCNow(), "phase", null);
         allTransitions.add(tr3);
 
-
         final List<Entitlement> entitlements = new ArrayList<Entitlement>();
         final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
         entitlements.add(entitlement);
 
-        final DefaultSubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, blockingStates);
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, blockingStates);
 
         assertEquals(timeline.getAccountId(), accountId);
         assertEquals(timeline.getBundleId(), bundleId);
         assertEquals(timeline.getExternalKey(), externalKey);
 
-        List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
+        final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
         assertEquals(events.size(), 5);
 
         assertEquals(events.get(0).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
@@ -470,18 +523,26 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
         assertEquals(events.get(4).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
 
+        assertEquals(events.get(0).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(1).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+        assertEquals(events.get(2).getServiceName(), DefaultSubscriptionBundleTimeline.ENT_BILLING_SERVICE_NAME);
+        assertEquals(events.get(3).getServiceName(), service);
+        assertEquals(events.get(4).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+
+        assertNull(events.get(0).getPrevPhase());
         assertEquals(events.get(0).getNextPhase().getName(), "trial");
+        assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial");
+        assertEquals(events.get(2).getPrevPhase().getName(), "trial");
         assertEquals(events.get(2).getNextPhase().getName(), "phase");
+        assertEquals(events.get(3).getPrevPhase().getName(), "phase");
         assertEquals(events.get(3).getNextPhase().getName(), "phase");
-        assertEquals(events.get(4).getNextPhase(), null);
+        assertEquals(events.get(4).getPrevPhase().getName(), "phase");
+        assertNull(events.get(4).getNextPhase());
     }
 
-
-
     @Test(groups = "fast")
     public void testOneEntitlementWithBlockingStatesSubscription() throws CatalogApiException {
-
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -489,7 +550,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final UUID bundleId = UUID.randomUUID();
         final String externalKey = "foo";
 
-
         final UUID entitlementId = UUID.randomUUID();
 
         final List<SubscriptionBaseTransition> allTransitions = new ArrayList<SubscriptionBaseTransition>();
@@ -526,10 +586,9 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
         entitlements.add(entitlement);
 
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, blockingStates);
 
-        final DefaultSubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, blockingStates);
-
-        List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
+        final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
         assertEquals(events.size(), 6);
 
         assertEquals(events.get(0).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
@@ -546,24 +605,32 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(4).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
         assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
 
-        assertEquals(events.get(0).getPrevPhase(), null);
+        assertEquals(events.get(0).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(1).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+        assertEquals(events.get(2).getServiceName(), DefaultSubscriptionBundleTimeline.ENT_BILLING_SERVICE_NAME);
+        assertEquals(events.get(3).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(4).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(5).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+
+        assertNull(events.get(0).getPrevPhase());
         assertEquals(events.get(0).getNextPhase().getName(), "trial");
-        assertEquals(events.get(1).getPrevPhase(), null);
+        assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial");
+
         assertEquals(events.get(2).getPrevPhase().getName(), "trial");
         assertEquals(events.get(2).getNextPhase().getName(), "phase");
+
         assertEquals(events.get(3).getPrevPhase().getName(), "phase");
         assertEquals(events.get(3).getNextPhase().getName(), "phase");
+
         assertEquals(events.get(4).getPrevPhase().getName(), "phase");
-        assertEquals(events.get(4).getNextPhase(), null);
+        assertNull(events.get(4).getNextPhase());
         assertEquals(events.get(5).getPrevPhase().getName(), "phase");
-        assertEquals(events.get(5).getNextPhase(), null);
+        assertNull(events.get(5).getNextPhase());
     }
 
-
     @Test(groups = "fast")
     public void testWithMultipleEntitlements() throws CatalogApiException {
-
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -571,7 +638,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final UUID bundleId = UUID.randomUUID();
         final String externalKey = "foo";
 
-
         final UUID entitlementId1 = UUID.fromString("cf5a597a-cf15-45d3-8f02-95371be7f927");
         final UUID entitlementId2 = UUID.fromString("e37cc97a-7b98-4ab6-a29a-7259e45c3366");
 
@@ -584,7 +650,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition ent1Tr1 = createTransition(entitlementId1, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial1");
         allTransitions1.add(ent1Tr1);
 
-
         effectiveDate = effectiveDate.plusDays(15);
         clock.addDays(15);
         final SubscriptionBaseTransition ent2Tr1 = createTransition(entitlementId2, EventType.API_USER, ApiEventType.TRANSFER, requestedDate, effectiveDate, clock.getUTCNow(), null, "phase2");
@@ -619,10 +684,9 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final Entitlement entitlement2 = createEntitlement(entitlementId2, allTransitions2);
         entitlements.add(entitlement2);
 
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, blockingStates);
 
-        final DefaultSubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, blockingStates);
-
-        List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
+        final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
         assertEquals(events.size(), 9);
 
         assertEquals(events.get(0).getEffectiveDate().compareTo(new LocalDate(ent1Tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
@@ -651,13 +715,27 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(7).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
         assertEquals(events.get(8).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
 
-        assertEquals(events.get(0).getPrevPhase(), null);
+        assertEquals(events.get(0).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(1).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+        assertEquals(events.get(2).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(3).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+
+        assertEquals(events.get(4).getServiceName(), DefaultSubscriptionBundleTimeline.ENT_BILLING_SERVICE_NAME);
+
+        assertEquals(events.get(5).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(6).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+
+        assertEquals(events.get(7).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(8).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+
+        assertNull(events.get(0).getPrevPhase());
         assertEquals(events.get(0).getNextPhase().getName(), "trial1");
-        assertEquals(events.get(1).getPrevPhase(), null);
+        assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial1");
-        assertEquals(events.get(2).getPrevPhase(), null);
+
+        assertNull(events.get(2).getPrevPhase());
         assertEquals(events.get(2).getNextPhase().getName(), "phase2");
-        assertEquals(events.get(3).getPrevPhase(), null);
+        assertNull(events.get(3).getPrevPhase());
         assertEquals(events.get(3).getNextPhase().getName(), "phase2");
 
         assertEquals(events.get(4).getPrevPhase().getName(), "trial1");
@@ -667,20 +745,17 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(5).getNextPhase().getName(), "phase1");
 
         assertEquals(events.get(6).getPrevPhase().getName(), "phase2");
-        assertEquals(events.get(6).getNextPhase(), null);
+        assertEquals(events.get(6).getNextPhase().getName(), "phase2");
 
         assertEquals(events.get(7).getPrevPhase().getName(), "phase1");
-        assertEquals(events.get(7).getNextPhase(), null);
+        assertNull(events.get(7).getNextPhase());
 
         assertEquals(events.get(8).getPrevPhase().getName(), "phase1");
-        assertEquals(events.get(8).getNextPhase(), null);
-
+        assertNull(events.get(8).getNextPhase());
     }
 
-
-
     @Test(groups = "fast")
-    public void testWithOverdueOfflineAndClear() throws CatalogApiException {
+    public void testWithOverdueOffline() throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -704,45 +779,24 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
         allTransitions.add(tr2);
 
-        effectiveDate = effectiveDate.plusDays(6); // 2013-02-06
-        clock.addDays(6);
+        effectiveDate = effectiveDate.plusDays(40); // 2013-03-12
+        clock.addDays(40);
         final SubscriptionBaseTransition tr3 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CANCEL, requestedDate, effectiveDate, clock.getUTCNow(), "phase", null);
         allTransitions.add(tr3);
 
-        effectiveDate = effectiveDate.plusDays(22);// 2013-02-28
-        clock.addDays(22);
-        final SubscriptionBaseTransition tr4 = createTransition(entitlementId, EventType.API_USER, ApiEventType.RE_CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "phase");
-        allTransitions.add(tr4);
-
-        effectiveDate = effectiveDate.plusDays(12); // 2013-03-12
-        clock.addDays(12);
-        final SubscriptionBaseTransition tr5 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CANCEL, requestedDate, effectiveDate, clock.getUTCNow(), "phase", null);
-        allTransitions.add(tr5);
-
+        final String service = "overdue-service";
         final BlockingState bs1 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.ACCOUNT,
-                                                           "OFFLINE", "overdue-service",
+                                                           "OFFLINE", service,
                                                            true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
 
         blockingStates.add(bs1);
 
-
         final BlockingState bs2 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
                                                            DefaultEntitlementApi.ENT_STATE_CANCELLED, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
                                                            true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
 
         blockingStates.add(bs2);
 
-
-        effectiveDate = effectiveDate.plusDays(12); // 2013-03-24
-        clock.addDays(12);
-
-        final BlockingState bs3 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.ACCOUNT,
-                                                           "__KILLBILL__CLEAR__OVERDUE__STATE__", "overdue-service",
-                                                           false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
-
-        blockingStates.add(bs3);
-
-
         final List<Entitlement> entitlements = new ArrayList<Entitlement>();
         final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
         entitlements.add(entitlement);
@@ -754,48 +808,132 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(timeline.getExternalKey(), externalKey);
 
         final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
-        assertEquals(events.size(), 11);
+        assertEquals(events.size(), 6);
 
         assertEquals(events.get(0).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
         assertEquals(events.get(1).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
         assertEquals(events.get(2).getEffectiveDate().compareTo(new LocalDate(tr2.getEffectiveTransitionTime(), accountTimeZone)), 0);
         assertEquals(events.get(3).getEffectiveDate().compareTo(new LocalDate(tr3.getEffectiveTransitionTime(), accountTimeZone)), 0);
         assertEquals(events.get(4).getEffectiveDate().compareTo(new LocalDate(tr3.getEffectiveTransitionTime(), accountTimeZone)), 0);
-        assertEquals(events.get(5).getEffectiveDate().compareTo(new LocalDate(tr4.getEffectiveTransitionTime(), accountTimeZone)), 0);
-        assertEquals(events.get(6).getEffectiveDate().compareTo(new LocalDate(tr4.getEffectiveTransitionTime(), accountTimeZone)), 0);
-        assertEquals(events.get(7).getEffectiveDate().compareTo(new LocalDate(tr5.getEffectiveTransitionTime(), accountTimeZone)), 0);
-        assertEquals(events.get(8).getEffectiveDate().compareTo(new LocalDate(tr5.getEffectiveTransitionTime(), accountTimeZone)), 0);
-        assertEquals(events.get(9).getEffectiveDate().compareTo(new LocalDate(tr5.getEffectiveTransitionTime(), accountTimeZone)), 0);
-        assertEquals(events.get(10).getEffectiveDate().compareTo(new LocalDate(bs3.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(5).getEffectiveDate().compareTo(new LocalDate(bs1.getEffectiveDate(), accountTimeZone)), 0);
 
         assertEquals(events.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
         assertEquals(events.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
         assertEquals(events.get(2).getSubscriptionEventType(), SubscriptionEventType.PHASE);
-        assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.PAUSE_ENTITLEMENT);
-        assertEquals(events.get(4).getSubscriptionEventType(), SubscriptionEventType.PAUSE_BILLING);
-        assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.RESUME_ENTITLEMENT);
-        assertEquals(events.get(6).getSubscriptionEventType(), SubscriptionEventType.RESUME_BILLING);
-        assertEquals(events.get(7).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
-        assertEquals(events.get(8).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
-        assertEquals(events.get(9).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
-        assertEquals(events.get(10).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
+        assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
+        assertEquals(events.get(4).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
+        assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
+
+        assertEquals(events.get(0).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(1).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+        assertEquals(events.get(2).getServiceName(), DefaultSubscriptionBundleTimeline.ENT_BILLING_SERVICE_NAME);
+        assertEquals(events.get(3).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(4).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+        assertEquals(events.get(5).getServiceName(), service);
 
+        assertNull(events.get(0).getPrevPhase());
         assertEquals(events.get(0).getNextPhase().getName(), "trial");
+        assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial");
+
+        assertEquals(events.get(2).getPrevPhase().getName(), "trial");
         assertEquals(events.get(2).getNextPhase().getName(), "phase");
-        assertEquals(events.get(3).getNextPhase(), null);
-        assertEquals(events.get(4).getNextPhase(), null);
-        assertEquals(events.get(5).getNextPhase().getName(), "phase");
-        assertEquals(events.get(6).getNextPhase().getName(), "phase");
-        assertEquals(events.get(7).getNextPhase(), null);
-        assertEquals(events.get(8).getNextPhase(), null);
-        assertEquals(events.get(9).getNextPhase(), null);
-        assertEquals(events.get(10).getNextPhase(), null);
+
+        assertEquals(events.get(3).getPrevPhase().getName(), "phase");
+        assertNull(events.get(3).getNextPhase());
+        assertEquals(events.get(4).getPrevPhase().getName(), "phase");
+        assertNull(events.get(4).getNextPhase());
+
+        assertEquals(events.get(5).getPrevPhase().getName(), "phase");
+        assertNull(events.get(5).getNextPhase());
     }
 
+    @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/134")
+    public void testRemoveOverlappingBlockingStates() throws CatalogApiException {
+        clock.setDay(new LocalDate(2013, 1, 1));
+
+        final DateTimeZone accountTimeZone = DateTimeZone.UTC;
+        final UUID accountId = UUID.randomUUID();
+        final UUID bundleId = UUID.randomUUID();
+        final String externalKey = "foo";
+
+        final UUID entitlementId = UUID.randomUUID();
+
+        final List<SubscriptionBaseTransition> allTransitions = new ArrayList<SubscriptionBaseTransition>();
+        final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
+
+        final DateTime requestedDate = new DateTime();
+        DateTime effectiveDate = new DateTime(2013, 1, 1, 15, 43, 25, 0, DateTimeZone.UTC);
+        final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
+        allTransitions.add(tr1);
+
+        // Overlapping ENT_STATE_BLOCKED - should merge
+        effectiveDate = effectiveDate.plusDays(5);
+        clock.addDays(5);
+        final BlockingState bs1 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                           DefaultEntitlementApi.ENT_STATE_BLOCKED, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                           true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+        blockingStates.add(bs1);
+
+        effectiveDate = effectiveDate.plusDays(1);
+        clock.addDays(1);
+        final BlockingState bs2 = new DefaultBlockingState(UUID.randomUUID(), bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                           DefaultEntitlementApi.ENT_STATE_BLOCKED, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                           true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+        blockingStates.add(bs2);
+
+        // Overlapping ENT_STATE_CANCELLED - should merge
+        effectiveDate = effectiveDate.plusDays(1);
+        clock.addDays(1);
+        final BlockingState bs3 = new DefaultBlockingState(UUID.randomUUID(), accountId, BlockingStateType.ACCOUNT,
+                                                           DefaultEntitlementApi.ENT_STATE_CANCELLED, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                           true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+        blockingStates.add(bs3);
+        final BlockingState bs4 = new DefaultBlockingState(UUID.randomUUID(), bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                           DefaultEntitlementApi.ENT_STATE_CANCELLED, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                           true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+        blockingStates.add(bs4);
+
+        final List<Entitlement> entitlements = new ArrayList<Entitlement>();
+        final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
+        entitlements.add(entitlement);
 
-    private DefaultEntitlement createEntitlement(final UUID entitlementId, final List<SubscriptionBaseTransition> allTransitions) {
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, blockingStates);
 
+        final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
+        assertEquals(events.size(), 4);
+
+        assertEquals(events.get(0).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(1).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(2).getEffectiveDate().compareTo(new LocalDate(bs1.getEffectiveDate(), accountTimeZone)), 0);
+        assertEquals(events.get(3).getEffectiveDate().compareTo(new LocalDate(bs3.getEffectiveDate(), accountTimeZone)), 0);
+
+        assertEquals(events.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
+        assertEquals(events.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
+        assertEquals(events.get(2).getSubscriptionEventType(), SubscriptionEventType.PAUSE_ENTITLEMENT);
+        assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
+
+        assertEquals(events.get(0).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(1).getServiceName(), DefaultSubscriptionBundleTimeline.BILLING_SERVICE_NAME);
+        assertEquals(events.get(2).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertEquals(events.get(3).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
+
+        assertNull(events.get(0).getPrevPhase());
+        assertEquals(events.get(0).getNextPhase().getName(), "trial");
+        assertNull(events.get(1).getPrevPhase());
+        assertEquals(events.get(1).getNextPhase().getName(), "trial");
+
+        assertEquals(events.get(2).getPrevPhase().getName(), "trial");
+        assertEquals(events.get(2).getNextPhase().getName(), "trial");
+
+        assertEquals(events.get(3).getPrevPhase().getName(), "trial");
+        assertNull(events.get(3).getNextPhase());
+    }
+
+    private Entitlement createEntitlement(final UUID entitlementId, final List<SubscriptionBaseTransition> allTransitions) {
         final DefaultEntitlement result = Mockito.mock(DefaultEntitlement.class);
         Mockito.when(result.getId()).thenReturn(entitlementId);
 
@@ -805,13 +943,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         return result;
     }
 
-    private String computePhaseName(final UUID entitlementId, final EventType eventType, final ApiEventType apiEventType) {
-        return "phase-" +
-               entitlementId.toString() +
-               "-" +
-               (eventType == EventType.API_USER ? apiEventType : eventType);
-    }
-
     private SubscriptionBaseTransition createTransition(final UUID entitlementId,
                                                         final EventType eventType,
                                                         final ApiEventType apiEventType,
@@ -821,58 +952,76 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
                                                         final String prevPhaseName,
                                                         final String nextPhaseName
                                                        ) throws CatalogApiException {
+        final PlanPhase prevPhase;
+        final Plan prevPlan;
+        final Product prevProduct;
+        final PriceList prevPriceList;
+        if (prevPhaseName == null) {
+            prevPhase = null;
+            prevPlan = null;
+            prevProduct = null;
+            prevPriceList = null;
+        } else {
+            prevPhase = Mockito.mock(PlanPhase.class);
+            Mockito.when(prevPhase.getName()).thenReturn(prevPhaseName);
 
+            prevProduct = Mockito.mock(Product.class);
+            Mockito.when(prevProduct.getName()).thenReturn("product");
 
-        final PlanPhase prevPhase = prevPhaseName != null ? Mockito.mock(PlanPhase.class) : null;
-        if (prevPhase != null) {
-            Mockito.when(prevPhase.getName()).thenReturn(prevPhaseName);
+            prevPlan = Mockito.mock(Plan.class);
+            Mockito.when(prevPlan.getName()).thenReturn("plan");
+            Mockito.when(prevPlan.getProduct()).thenReturn(prevProduct);
+
+            prevPriceList = Mockito.mock(PriceList.class);
+            Mockito.when(prevPriceList.getName()).thenReturn("pricelist");
         }
 
-        final PlanPhase nextPhase = nextPhaseName != null ? Mockito.mock(PlanPhase.class) : null;
-        if (nextPhase !=  null) {
+        final PlanPhase nextPhase;
+        final Plan nextPlan;
+        final Product nextProduct;
+        final PriceList nextPriceList;
+        if (nextPhaseName == null) {
+            nextPhase = null;
+            nextPlan = null;
+            nextProduct = null;
+            nextPriceList = null;
+        } else {
+            nextPhase = Mockito.mock(PlanPhase.class);
             Mockito.when(nextPhase.getName()).thenReturn(nextPhaseName);
+
+            nextProduct = Mockito.mock(Product.class);
+            Mockito.when(nextProduct.getName()).thenReturn("product");
+
+            nextPlan = Mockito.mock(Plan.class);
+            Mockito.when(nextPlan.getName()).thenReturn("plan");
+            Mockito.when(nextPlan.getProduct()).thenReturn(nextProduct);
+
+            nextPriceList = Mockito.mock(PriceList.class);
+            Mockito.when(nextPriceList.getName()).thenReturn("pricelist");
         }
 
-        //catalogService.getCurrentCatalog().findCurrentPhase("pistol-monthly-trial");
-        final Plan plan = Mockito.mock(Plan.class);
-        Mockito.when(plan.getName()).thenReturn("plan");
-
-
-        //catalogService.getCurrentCatalog().findCurrentPlan("pistol-monthly");
-        final Product product = Mockito.mock(Product.class);
-        Mockito.when(product.getName()).thenReturn("product");
-
-        //catalogService.getCurrentCatalog().findCurrentProduct("Pistol");
-
-        final PriceList priceList = Mockito.mock(PriceList.class);
-        Mockito.when(priceList.getName()).thenReturn("pricelist");
-
-        //catalogService.getCurrentCatalog().findCurrentPricelist(PriceListSet.DEFAULT_PRICELIST_NAME);
-        final BillingPeriod billingPeriod = BillingPeriod.MONTHLY;
-
-        final SubscriptionBaseTransition transition = new SubscriptionBaseTransitionData(UUID.randomUUID(),
-                                                                                         entitlementId,
-                                                                                         bundleId,
-                                                                                         eventType,
-                                                                                         apiEventType,
-                                                                                         requestedDate,
-                                                                                         effectiveDate,
-                                                                                         null,
-                                                                                         null,
-                                                                                         null,
-                                                                                         plan,
-                                                                                         prevPhase,
-                                                                                         priceList,
-                                                                                         null,
-                                                                                         null,
-                                                                                         null,
-                                                                                         plan,
-                                                                                         nextPhase,
-                                                                                         priceList,
-                                                                                         1L,
-                                                                                         createdDate,
-                                                                                         UUID.randomUUID(),
-                                                                                         true);
-        return transition;
+        return new SubscriptionBaseTransitionData(UUID.randomUUID(),
+                                                  entitlementId,
+                                                  bundleId,
+                                                  eventType,
+                                                  apiEventType,
+                                                  requestedDate,
+                                                  effectiveDate,
+                                                  null,
+                                                  null,
+                                                  null,
+                                                  prevPlan,
+                                                  prevPhase,
+                                                  prevPriceList,
+                                                  null,
+                                                  null,
+                                                  null,
+                                                  nextPlan,
+                                                  nextPhase,
+                                                  nextPriceList,
+                                                  1L,
+                                                  createdDate,
+                                                  UUID.randomUUID(),
+                                                  true);
     }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java b/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
index 99711d9..e0396c1 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
@@ -52,6 +52,7 @@ import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 import com.ning.billing.subscription.api.SubscriptionBaseService;
 import com.ning.billing.subscription.engine.core.DefaultSubscriptionBaseService;
 import com.ning.billing.tag.TagInternalApi;
+import com.ning.billing.util.api.AuditUserApi;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.svcsapi.bus.BusService;
 import com.ning.billing.util.tag.dao.TagDao;
@@ -108,6 +109,8 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
     @Inject
     protected EventsStreamBuilder eventsStreamBuilder;
     @Inject
+    protected AuditUserApi auditUserApi;
+    @Inject
     protected InternalCallContextFactory internalCallContextFactory;
 
     protected Catalog catalog;
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEntitlementModuleWithEmbeddedDB.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEntitlementModuleWithEmbeddedDB.java
index 2369cb6..0b40737 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEntitlementModuleWithEmbeddedDB.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEntitlementModuleWithEmbeddedDB.java
@@ -25,6 +25,7 @@ import com.ning.billing.api.TestListenerStatus;
 import com.ning.billing.catalog.glue.CatalogModule;
 import com.ning.billing.entitlement.EntitlementTestListenerStatus;
 import com.ning.billing.subscription.glue.DefaultSubscriptionModule;
+import com.ning.billing.util.glue.AuditModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.MetricsModule;
 import com.ning.billing.util.glue.NonEntityDaoModule;
@@ -49,6 +50,7 @@ public class TestEntitlementModuleWithEmbeddedDB extends TestEntitlementModule {
         install(new CatalogModule(configSource));
         install(new NotificationQueueModule(configSource));
         install(new DefaultSubscriptionModule(configSource));
+        install(new AuditModule());
 
         bind(TestListenerStatus.class).to(EntitlementTestListenerStatus.class).asEagerSingleton();
         bind(TestApiListener.class).asEagerSingleton();

invoice/pom.xml 2(+1 -1)

diff --git a/invoice/pom.xml b/invoice/pom.xml
index 1f78927..030497f 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>

jaxrs/pom.xml 2(+1 -1)

diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index 345c7ca..68b4477 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-jaxrs</artifactId>

junction/pom.xml 2(+1 -1)

diff --git a/junction/pom.xml b/junction/pom.xml
index fb6b57f..e187bd6 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-junction</artifactId>

NEWS 6(+6 -0)

diff --git a/NEWS b/NEWS
index c528d75..3a050f2 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,9 @@
+0.8.5
+    https://github.com/killbill/killbill/issues/134
+    https://github.com/killbill/killbill/issues/135
+    https://github.com/killbill/killbill/issues/136
+    https://github.com/killbill/killbill/issues/140
+
 0.8.4
     Subscription base speedups
     Fix regression in bundle timeline API

osgi/pom.xml 2(+1 -1)

diff --git a/osgi/pom.xml b/osgi/pom.xml
index 9a64008..10b9dd3 100644
--- a/osgi/pom.xml
+++ b/osgi/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi</artifactId>
diff --git a/osgi-bundles/bundles/jruby/pom.xml b/osgi-bundles/bundles/jruby/pom.xml
index 1359ab5..fd3ade3 100644
--- a/osgi-bundles/bundles/jruby/pom.xml
+++ b/osgi-bundles/bundles/jruby/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill-osgi-bundles</artifactId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-jruby</artifactId>
diff --git a/osgi-bundles/bundles/logger/pom.xml b/osgi-bundles/bundles/logger/pom.xml
index 50b2f75..bc85013 100644
--- a/osgi-bundles/bundles/logger/pom.xml
+++ b/osgi-bundles/bundles/logger/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-logger</artifactId>
diff --git a/osgi-bundles/bundles/pom.xml b/osgi-bundles/bundles/pom.xml
index 1a4372a..f582871 100644
--- a/osgi-bundles/bundles/pom.xml
+++ b/osgi-bundles/bundles/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-all-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles</artifactId>
diff --git a/osgi-bundles/bundles/webconsolebranding/pom.xml b/osgi-bundles/bundles/webconsolebranding/pom.xml
index 0065bd1..8f4d304 100644
--- a/osgi-bundles/bundles/webconsolebranding/pom.xml
+++ b/osgi-bundles/bundles/webconsolebranding/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-webconsolebranding</artifactId>
diff --git a/osgi-bundles/defaultbundles/pom.xml b/osgi-bundles/defaultbundles/pom.xml
index 8d497e9..2447276 100644
--- a/osgi-bundles/defaultbundles/pom.xml
+++ b/osgi-bundles/defaultbundles/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-all-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-defaultbundles</artifactId>
diff --git a/osgi-bundles/libs/killbill/pom.xml b/osgi-bundles/libs/killbill/pom.xml
index a1ce27b..1d327f7 100644
--- a/osgi-bundles/libs/killbill/pom.xml
+++ b/osgi-bundles/libs/killbill/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-lib-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-lib-killbill</artifactId>
diff --git a/osgi-bundles/libs/pom.xml b/osgi-bundles/libs/pom.xml
index 7796ac0..655a061 100644
--- a/osgi-bundles/libs/pom.xml
+++ b/osgi-bundles/libs/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-all-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-lib-bundles</artifactId>
diff --git a/osgi-bundles/libs/slf4j-osgi/pom.xml b/osgi-bundles/libs/slf4j-osgi/pom.xml
index 4712edb..22a896f 100644
--- a/osgi-bundles/libs/slf4j-osgi/pom.xml
+++ b/osgi-bundles/libs/slf4j-osgi/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-lib-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-lib-slf4j-osgi</artifactId>
diff --git a/osgi-bundles/pom.xml b/osgi-bundles/pom.xml
index 4232e6b..c8d28b6 100644
--- a/osgi-bundles/pom.xml
+++ b/osgi-bundles/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-all-bundles</artifactId>
diff --git a/osgi-bundles/tests/beatrix/pom.xml b/osgi-bundles/tests/beatrix/pom.xml
index 0260789..a4e9c33 100644
--- a/osgi-bundles/tests/beatrix/pom.xml
+++ b/osgi-bundles/tests/beatrix/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-test-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-test-beatrix</artifactId>
diff --git a/osgi-bundles/tests/payment/pom.xml b/osgi-bundles/tests/payment/pom.xml
index dcf75c8..0a17689 100644
--- a/osgi-bundles/tests/payment/pom.xml
+++ b/osgi-bundles/tests/payment/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-test-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-test-payment</artifactId>
diff --git a/osgi-bundles/tests/pom.xml b/osgi-bundles/tests/pom.xml
index 664c307..629cbc8 100644
--- a/osgi-bundles/tests/pom.xml
+++ b/osgi-bundles/tests/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-all-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-test-bundles</artifactId>

overdue/pom.xml 2(+1 -1)

diff --git a/overdue/pom.xml b/overdue/pom.xml
index b7c00f4..7a324dd 100644
--- a/overdue/pom.xml
+++ b/overdue/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-overdue</artifactId>

payment/pom.xml 2(+1 -1)

diff --git a/payment/pom.xml b/payment/pom.xml
index d937594..36e140f 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>
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 23a067b..b47865b 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
@@ -158,7 +158,7 @@ public class DefaultPayment extends EntityBase implements Payment {
 
         BigDecimal result = amount;
         for (RefundModelDao cur : refunds) {
-            if (cur.getRefundStatus() != RefundStatus.CREATED) {
+            if (cur.getRefundStatus() == RefundStatus.COMPLETED) {
                 result = result.subtract(cur.getAmount());
             }
         }

pom.xml 4(+2 -2)

diff --git a/pom.xml b/pom.xml
index 220af20..858d7fd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,10 +19,10 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.5.7</version>
+        <version>0.5.8</version>
     </parent>
     <artifactId>killbill</artifactId>
-    <version>0.8.5-SNAPSHOT</version>
+    <version>0.8.6-SNAPSHOT</version>
     <packaging>pom</packaging>
     <name>killbill</name>
     <description>Library for managing recurring subscriptions and the associated billing</description>

server/pom.xml 2(+1 -1)

diff --git a/server/pom.xml b/server/pom.xml
index b1b9f66..12e0b5a 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-server</artifactId>
diff --git a/subscription/pom.xml b/subscription/pom.xml
index cfa35c4..856419f 100644
--- a/subscription/pom.xml
+++ b/subscription/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-subscription</artifactId>

tenant/pom.xml 2(+1 -1)

diff --git a/tenant/pom.xml b/tenant/pom.xml
index 5907887..082ca08 100644
--- a/tenant/pom.xml
+++ b/tenant/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-tenant</artifactId>

usage/pom.xml 2(+1 -1)

diff --git a/usage/pom.xml b/usage/pom.xml
index 02850db..1824d27 100644
--- a/usage/pom.xml
+++ b/usage/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-usage</artifactId>

util/pom.xml 2(+1 -1)

diff --git a/util/pom.xml b/util/pom.xml
index 1f89950..61bf29f 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -12,7 +12,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.5-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>