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 12(+11 -1)

NEWS 16(+16 -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 a66537e..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.3-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-account</artifactId>
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountEmailModelDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountEmailModelDao.java
index 4962a24..6b2bd31 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountEmailModelDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountEmailModelDao.java
@@ -54,6 +54,18 @@ public class AccountEmailModelDao extends EntityBase implements EntityModelDao<A
         return isActive;
     }
 
+    public void setAccountId(final UUID accountId) {
+        this.accountId = accountId;
+    }
+
+    public void setEmail(final String email) {
+        this.email = email;
+    }
+
+    public void setIsActive(final Boolean isActive) {
+        this.isActive = isActive;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountModelDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountModelDao.java
index 57365eb..2996fec 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountModelDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountModelDao.java
@@ -176,6 +176,82 @@ public class AccountModelDao extends EntityBase implements EntityModelDao<Accoun
         return isNotifiedForInvoices;
     }
 
+    public void setExternalKey(final String externalKey) {
+        this.externalKey = externalKey;
+    }
+
+    public void setEmail(final String email) {
+        this.email = email;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public void setFirstNameLength(final Integer firstNameLength) {
+        this.firstNameLength = firstNameLength;
+    }
+
+    public void setCurrency(final Currency currency) {
+        this.currency = currency;
+    }
+
+    public void setBillingCycleDayLocal(final int billingCycleDayLocal) {
+        this.billingCycleDayLocal = billingCycleDayLocal;
+    }
+
+    public void setPaymentMethodId(final UUID paymentMethodId) {
+        this.paymentMethodId = paymentMethodId;
+    }
+
+    public void setTimeZone(final DateTimeZone timeZone) {
+        this.timeZone = timeZone;
+    }
+
+    public void setLocale(final String locale) {
+        this.locale = locale;
+    }
+
+    public void setAddress1(final String address1) {
+        this.address1 = address1;
+    }
+
+    public void setAddress2(final String address2) {
+        this.address2 = address2;
+    }
+
+    public void setCompanyName(final String companyName) {
+        this.companyName = companyName;
+    }
+
+    public void setCity(final String city) {
+        this.city = city;
+    }
+
+    public void setStateOrProvince(final String stateOrProvince) {
+        this.stateOrProvince = stateOrProvince;
+    }
+
+    public void setCountry(final String country) {
+        this.country = country;
+    }
+
+    public void setPostalCode(final String postalCode) {
+        this.postalCode = postalCode;
+    }
+
+    public void setPhone(final String phone) {
+        this.phone = phone;
+    }
+
+    public void setMigrated(final Boolean migrated) {
+        this.migrated = migrated;
+    }
+
+    public void setIsNotifiedForInvoices(final Boolean isNotifiedForInvoices) {
+        this.isNotifiedForInvoices = isNotifiedForInvoices;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();

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

diff --git a/api/pom.xml b/api/pom.xml
index fed08a0..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.3-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-internal-api</artifactId>
diff --git a/api/src/main/java/com/ning/billing/entitlement/AccountEntitlements.java b/api/src/main/java/com/ning/billing/entitlement/AccountEntitlements.java
new file mode 100644
index 0000000..3a192e9
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/AccountEntitlements.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.entitlement.api.Entitlement;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+
+// Wrapper object to save on DAO calls
+public interface AccountEntitlements {
+
+    public Account getAccount();
+
+    // Map bundle id -> bundle
+    public Map<UUID, SubscriptionBaseBundle> getBundles();
+
+    // Map bundle id -> entitlements
+    public Map<UUID, Collection<Entitlement>> getEntitlements();
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/AccountEventsStreams.java b/api/src/main/java/com/ning/billing/entitlement/AccountEventsStreams.java
new file mode 100644
index 0000000..94e7817
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/AccountEventsStreams.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+
+// Wrapper object to save on DAO calls
+public interface AccountEventsStreams {
+
+    public Account getAccount();
+
+    // Map bundle id -> bundle
+    public Map<UUID, SubscriptionBaseBundle> getBundles();
+
+    // Map bundle id -> events streams
+    public Map<UUID, Collection<EventsStream>> getEventsStreams();
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/EntitlementInternalApi.java b/api/src/main/java/com/ning/billing/entitlement/EntitlementInternalApi.java
new file mode 100644
index 0000000..44953ea
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/EntitlementInternalApi.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+import java.util.UUID;
+
+import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.util.callcontext.TenantContext;
+
+public interface EntitlementInternalApi {
+
+    public AccountEntitlements getAllEntitlementsForAccountId(UUID accountId, TenantContext context) throws EntitlementApiException;
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/EventsStream.java b/api/src/main/java/com/ning/billing/entitlement/EventsStream.java
new file mode 100644
index 0000000..8cb930f
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/EventsStream.java
@@ -0,0 +1,73 @@
+/*
+ * 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;
+
+import java.util.Collection;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+
+import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.entitlement.api.BlockingState;
+import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
+import com.ning.billing.subscription.api.SubscriptionBase;
+
+public interface EventsStream {
+
+    UUID getAccountId();
+
+    DateTimeZone getAccountTimeZone();
+
+    UUID getBundleId();
+
+    String getBundleExternalKey();
+
+    UUID getEntitlementId();
+
+    EntitlementState getEntitlementState();
+
+    LocalDate getEntitlementEffectiveEndDate();
+
+    SubscriptionBase getSubscription();
+
+    SubscriptionBase getBaseSubscription();
+
+    boolean isEntitlementActive();
+
+    boolean isBlockChange();
+
+    boolean isEntitlementCancelled();
+
+    boolean isSubscriptionCancelled();
+
+    Collection<BlockingState> getCurrentSubscriptionEntitlementBlockingStatesForServices();
+
+    Collection<BlockingState> getPendingEntitlementCancellationEvents();
+
+    BlockingState getEntitlementCancellationEvent();
+
+    // All blocking states for the account, associated bundle or subscription
+    Collection<BlockingState> getBlockingStates();
+
+    Collection<BlockingState> computeAddonsBlockingStatesForNextSubscriptionBaseEvent(DateTime effectiveDate);
+
+    Collection<BlockingState> computeAddonsBlockingStatesForFutureSubscriptionBaseEvents();
+
+    InternalTenantContext getInternalTenantContext();
+}
diff --git a/api/src/main/java/com/ning/billing/entity/EntityBase.java b/api/src/main/java/com/ning/billing/entity/EntityBase.java
index a579241..b0f4fea 100644
--- a/api/src/main/java/com/ning/billing/entity/EntityBase.java
+++ b/api/src/main/java/com/ning/billing/entity/EntityBase.java
@@ -24,9 +24,9 @@ import com.ning.billing.util.entity.Entity;
 
 public abstract class EntityBase implements Entity {
 
-    protected final UUID id;
-    protected final DateTime createdDate;
-    protected final DateTime updatedDate;
+    protected UUID id;
+    protected DateTime createdDate;
+    protected DateTime updatedDate;
 
     // used to hydrate objects
     public EntityBase(final UUID id) {
@@ -65,6 +65,18 @@ public abstract class EntityBase implements Entity {
         return updatedDate;
     }
 
+    public void setId(final UUID id) {
+        this.id = id;
+    }
+
+    public void setCreatedDate(final DateTime createdDate) {
+        this.createdDate = createdDate;
+    }
+
+    public void setUpdatedDate(final DateTime updatedDate) {
+        this.updatedDate = updatedDate;
+    }
+
     @Override
     public boolean equals(final Object o) {
         if (this == o) {
diff --git a/api/src/main/java/com/ning/billing/events/BusInternalEvent.java b/api/src/main/java/com/ning/billing/events/BusInternalEvent.java
index 0a798ad..6867096 100644
--- a/api/src/main/java/com/ning/billing/events/BusInternalEvent.java
+++ b/api/src/main/java/com/ning/billing/events/BusInternalEvent.java
@@ -38,6 +38,7 @@ public interface BusInternalEvent extends BusEvent {
         INVOICE_EMPTY,
         OVERDUE_CHANGE,
         PAYMENT_ERROR,
+        PAYMENT_PLUGIN_ERROR,
         PAYMENT_INFO,
         SUBSCRIPTION_TRANSITION,
         USER_TAGDEFINITION_CREATION,
diff --git a/api/src/main/java/com/ning/billing/glue/EntitlementModule.java b/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
index 456fb17..bd21d81 100644
--- a/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
+++ b/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
@@ -17,12 +17,15 @@
 package com.ning.billing.glue;
 
 public interface EntitlementModule {
+
     public void installBlockingStateDao();
 
     public void installBlockingApi();
 
     public void installEntitlementApi();
 
+    public void installEntitlementInternalApi();
+
     public void installSubscriptionApi();
 
     public void installBlockingChecker();
diff --git a/api/src/main/java/com/ning/billing/glue/SubscriptionModule.java b/api/src/main/java/com/ning/billing/glue/SubscriptionModule.java
index a400b68..65185eb 100644
--- a/api/src/main/java/com/ning/billing/glue/SubscriptionModule.java
+++ b/api/src/main/java/com/ning/billing/glue/SubscriptionModule.java
@@ -27,5 +27,4 @@ public interface SubscriptionModule {
     public void installSubscriptionInternalApi();
 
     public void installSubscriptionTimelineApi();
-
 }
diff --git a/api/src/main/java/com/ning/billing/junction/BillingEventSet.java b/api/src/main/java/com/ning/billing/junction/BillingEventSet.java
index 8dfca08..237e658 100644
--- a/api/src/main/java/com/ning/billing/junction/BillingEventSet.java
+++ b/api/src/main/java/com/ning/billing/junction/BillingEventSet.java
@@ -25,7 +25,4 @@ public interface BillingEventSet extends SortedSet<BillingEvent> {
     public abstract boolean isAccountAutoInvoiceOff();
 
     public abstract List<UUID> getSubscriptionIdsWithAutoInvoiceOff();
-
-    public boolean isLast(BillingEvent event);
-
 }
diff --git a/api/src/main/java/com/ning/billing/junction/BlockingInternalApi.java b/api/src/main/java/com/ning/billing/junction/BlockingInternalApi.java
index 0d30cf4..e7b9720 100644
--- a/api/src/main/java/com/ning/billing/junction/BlockingInternalApi.java
+++ b/api/src/main/java/com/ning/billing/junction/BlockingInternalApi.java
@@ -28,7 +28,7 @@ public interface BlockingInternalApi {
 
     public BlockingState getBlockingStateForService(UUID blockableId, BlockingStateType blockingStateType, String serviceName, InternalTenantContext context);
 
-    public List<BlockingState> getBlockingAll(UUID blockableId, BlockingStateType blockingStateType, InternalTenantContext context);
+    public List<BlockingState> getBlockingAllForAccount(InternalTenantContext context);
 
     public void setBlockingState(BlockingState state, InternalCallContext context);
 }

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

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 4a6b0f8..fae6f5e 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.3-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>
diff --git a/beatrix/src/main/java/com/ning/billing/beatrix/extbus/BeatrixListener.java b/beatrix/src/main/java/com/ning/billing/beatrix/extbus/BeatrixListener.java
index ea3db2d..b21288c 100644
--- a/beatrix/src/main/java/com/ning/billing/beatrix/extbus/BeatrixListener.java
+++ b/beatrix/src/main/java/com/ning/billing/beatrix/extbus/BeatrixListener.java
@@ -48,6 +48,7 @@ import com.ning.billing.events.InvoiceCreationInternalEvent;
 import com.ning.billing.events.OverdueChangeInternalEvent;
 import com.ning.billing.events.PaymentErrorInternalEvent;
 import com.ning.billing.events.PaymentInfoInternalEvent;
+import com.ning.billing.events.PaymentPluginErrorInternalEvent;
 import com.ning.billing.events.SubscriptionInternalEvent;
 import com.ning.billing.events.UserTagCreationInternalEvent;
 import com.ning.billing.events.UserTagDeletionInternalEvent;
@@ -182,6 +183,13 @@ public class BeatrixListener {
                 eventBusType = ExtBusEventType.PAYMENT_FAILED;
                 break;
 
+            case PAYMENT_PLUGIN_ERROR:
+                PaymentPluginErrorInternalEvent realEventPayPluginErr = (PaymentPluginErrorInternalEvent) event;
+                objectType = ObjectType.PAYMENT;
+                objectId = realEventPayPluginErr.getPaymentId();
+                eventBusType = ExtBusEventType.PAYMENT_FAILED;
+                break;
+
             case OVERDUE_CHANGE:
                 OverdueChangeInternalEvent realEventOC = (OverdueChangeInternalEvent) event;
                 objectType = ObjectType.ACCOUNT;

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

diff --git a/catalog/pom.xml b/catalog/pom.xml
index 011fe1d..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.3-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 e3e6379..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.3-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 1540e00..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.3-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/DefaultEntitlement.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
index 983381d..5576a98 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
@@ -21,10 +21,10 @@ import java.util.Collection;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 
 import com.ning.billing.ErrorCode;
-import com.ning.billing.account.api.Account;
 import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.catalog.api.BillingActionPolicy;
 import com.ning.billing.catalog.api.BillingPeriod;
@@ -36,12 +36,12 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.clock.Clock;
 import com.ning.billing.entitlement.DefaultEntitlementService;
 import com.ning.billing.entitlement.EntitlementService;
+import com.ning.billing.entitlement.EventsStream;
 import com.ning.billing.entitlement.block.BlockingChecker;
 import com.ning.billing.entitlement.dao.BlockingStateDao;
 import com.ning.billing.entitlement.engine.core.EntitlementNotificationKey;
 import com.ning.billing.entitlement.engine.core.EntitlementNotificationKeyAction;
 import com.ning.billing.entitlement.engine.core.EntitlementUtils;
-import com.ning.billing.entitlement.engine.core.EventsStream;
 import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
 import com.ning.billing.entity.EntityBase;
 import com.ning.billing.junction.DefaultBlockingState;
@@ -88,7 +88,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                               final SubscriptionBaseInternalApi subscriptionInternalApi, final BlockingChecker checker,
                               final NotificationQueueService notificationQueueService, final EntitlementUtils entitlementUtils,
                               final EntitlementDateHelper dateHelper, final Clock clock, final InternalCallContextFactory internalCallContextFactory) {
-        super(eventsStream.getSubscription().getId(), eventsStream.getSubscription().getCreatedDate(), eventsStream.getSubscription().getUpdatedDate());
+        super(eventsStream.getEntitlementId(), eventsStream.getSubscription().getCreatedDate(), eventsStream.getSubscription().getUpdatedDate());
         this.eventsStreamBuilder = eventsStreamBuilder;
         this.eventsStream = eventsStream;
         this.dateHelper = dateHelper;
@@ -116,18 +116,22 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
              in.getInternalCallContextFactory());
     }
 
-    public Account getAccount() {
-        return eventsStream.getAccount();
-    }
-
     public EventsStream getEventsStream() {
         return eventsStream;
     }
 
+    public DateTimeZone getAccountTimeZone() {
+        return eventsStream.getAccountTimeZone();
+    }
+
     public SubscriptionBase getSubscriptionBase() {
         return eventsStream.getSubscription();
     }
 
+    public SubscriptionBase getBaseSubscription() {
+        return eventsStream.getBaseSubscription();
+    }
+
     public EventsStreamBuilder getEventsStreamBuilder() {
         return eventsStreamBuilder;
     }
@@ -170,22 +174,22 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
 
     @Override
     public UUID getBaseEntitlementId() {
-        return eventsStream.getSubscription().getId();
+        return eventsStream.getEntitlementId();
     }
 
     @Override
     public UUID getBundleId() {
-        return eventsStream.getSubscription().getBundleId();
+        return eventsStream.getBundleId();
     }
 
     @Override
     public UUID getAccountId() {
-        return eventsStream.getAccount().getId();
+        return eventsStream.getAccountId();
     }
 
     @Override
     public String getExternalKey() {
-        return eventsStream.getBundle().getExternalKey();
+        return eventsStream.getBundleExternalKey();
     }
 
     @Override
@@ -200,7 +204,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
 
     @Override
     public LocalDate getEffectiveStartDate() {
-        return new LocalDate(getSubscriptionBase().getStartDate(), eventsStream.getAccount().getTimeZone());
+        return new LocalDate(getSubscriptionBase().getStartDate(), eventsStream.getAccountTimeZone());
     }
 
     @Override
@@ -285,15 +289,28 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         // Get the latest state from disk
         refresh(callContext);
 
-        if (eventsStream.isEntitlementCancelled() || eventsStream.isSubscriptionCancelled()) {
+        if (eventsStream.isSubscriptionCancelled()) {
             throw new EntitlementApiException(ErrorCode.SUB_CANCEL_BAD_STATE, getId(), EntitlementState.CANCELLED);
         }
 
-        // Reactivate entitlements
-        // See also https://github.com/killbill/killbill/issues/111
         final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
-        for (final BlockingState futureCancellation : eventsStream.getPendingEntitlementCancellationEvents()) {
-            blockingStateDao.unactiveBlockingState(futureCancellation.getId(), contextWithValidAccountRecordId);
+        final Collection<BlockingState> pendingEntitlementCancellationEvents = eventsStream.getPendingEntitlementCancellationEvents();
+        if (eventsStream.isEntitlementCancelled()) {
+            final BlockingState cancellationEvent = eventsStream.getEntitlementCancellationEvent();
+            blockingStateDao.unactiveBlockingState(cancellationEvent.getId(), contextWithValidAccountRecordId);
+        } else if (pendingEntitlementCancellationEvents.size() > 0) {
+            // Reactivate entitlements
+            // See also https://github.com/killbill/killbill/issues/111
+            //
+            // Today we only support cancellation at SUBSCRIPTION level (Not ACCOUNT or BUNDLE), so we should really have only
+            // one future event in the list
+            //
+            for (final BlockingState futureCancellation : pendingEntitlementCancellationEvents) {
+                blockingStateDao.unactiveBlockingState(futureCancellation.getId(), contextWithValidAccountRecordId);
+            }
+        } else {
+            // Entitlement is NOT cancelled (or future cancelled), there is nothing to do
+            throw new EntitlementApiException(ErrorCode.SUB_CANCEL_BAD_STATE, getId(), EntitlementState.CANCELLED);
         }
 
         // If billing was previously cancelled, reactivate
@@ -319,7 +336,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         // (we don't want an entitlement cancel date one second or so after the subscription cancel date or add-ons cancellations
         // computations won't work).
         final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
-        final LocalDate effectiveLocalDate = new LocalDate(localCancelDate, eventsStream.getAccount().getTimeZone());
+        final LocalDate effectiveLocalDate = new LocalDate(localCancelDate, eventsStream.getAccountTimeZone());
         final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(effectiveLocalDate, getSubscriptionBase().getStartDate(), contextWithValidAccountRecordId);
 
         try {
@@ -341,10 +358,10 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         final LocalDate cancellationDate;
         switch (entitlementPolicy) {
             case IMMEDIATE:
-                cancellationDate = new LocalDate(clock.getUTCNow(), eventsStream.getAccount().getTimeZone());
+                cancellationDate = new LocalDate(clock.getUTCNow(), eventsStream.getAccountTimeZone());
                 break;
             case END_OF_TERM:
-                cancellationDate = getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), eventsStream.getAccount().getTimeZone()) : new LocalDate(clock.getUTCNow(), eventsStream.getAccount().getTimeZone());
+                cancellationDate = getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), eventsStream.getAccountTimeZone()) : new LocalDate(clock.getUTCNow(), eventsStream.getAccountTimeZone());
                 break;
             default:
                 throw new RuntimeException("Unsupported policy " + entitlementPolicy);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
index 5be1c0e..dfd9cd6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
@@ -17,9 +17,7 @@
 package com.ning.billing.entitlement.api;
 
 import java.io.IOException;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 import javax.inject.Inject;
@@ -41,15 +39,16 @@ import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.BillingActionPolicy;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.AccountEventsStreams;
 import com.ning.billing.entitlement.DefaultEntitlementService;
 import com.ning.billing.entitlement.EntitlementService;
 import com.ning.billing.entitlement.EntitlementTransitionType;
+import com.ning.billing.entitlement.EventsStream;
 import com.ning.billing.entitlement.block.BlockingChecker;
 import com.ning.billing.entitlement.dao.BlockingStateDao;
 import com.ning.billing.entitlement.engine.core.EntitlementNotificationKey;
 import com.ning.billing.entitlement.engine.core.EntitlementNotificationKeyAction;
 import com.ning.billing.entitlement.engine.core.EntitlementUtils;
-import com.ning.billing.entitlement.engine.core.EventsStream;
 import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
 import com.ning.billing.junction.DefaultBlockingState;
 import com.ning.billing.notificationq.api.NotificationEvent;
@@ -141,8 +140,8 @@ public class DefaultEntitlementApi implements EntitlementApi {
         }
 
         // Check the base entitlement state is not blocked
-        if (eventsStreamForBaseSubscription.getCurrentBlockingAggregator().isBlockChange()) {
-            throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getSubscription().getId().toString()));
+        if (eventsStreamForBaseSubscription.isBlockChange()) {
+            throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getEntitlementId().toString()));
         }
 
         final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(effectiveDate, eventsStreamForBaseSubscription.getSubscription().getStartDate(), eventsStreamForBaseSubscription.getInternalTenantContext());
@@ -184,8 +183,21 @@ public class DefaultEntitlementApi implements EntitlementApi {
 
     @Override
     public List<Entitlement> getAllEntitlementsForBundle(final UUID bundleId, final TenantContext tenantContext) throws EntitlementApiException {
-        final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(tenantContext);
-        return getAllEntitlementsForBundle(subscriptionInternalApi.getSubscriptionsForBundle(bundleId, context), tenantContext);
+        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(tenantContext);
+        final UUID accountId;
+        try {
+            accountId = subscriptionInternalApi.getBundleFromId(bundleId, internalContext).getAccountId();
+        } catch (SubscriptionBaseApiException e) {
+            throw new EntitlementApiException(e);
+        }
+
+        return ImmutableList.<Entitlement>copyOf(Iterables.<Entitlement>filter(getAllEntitlementsForAccountId(accountId, tenantContext),
+                                                                               new Predicate<Entitlement>() {
+                                                                                   @Override
+                                                                                   public boolean apply(final Entitlement input) {
+                                                                                       return bundleId.equals(input.getBundleId());
+                                                                                   }
+                                                                               }));
     }
 
     @Override
@@ -202,29 +214,20 @@ public class DefaultEntitlementApi implements EntitlementApi {
 
     @Override
     public List<Entitlement> getAllEntitlementsForAccountId(final UUID accountId, final TenantContext tenantContext) throws EntitlementApiException {
+        final EntitlementApi entitlementApi = this;
         final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
-        final Map<UUID, List<SubscriptionBase>> subscriptionsPerBundle = subscriptionInternalApi.getSubscriptionsForAccount(context);
-
-        final List<Entitlement> result = new LinkedList<Entitlement>();
-        for (final UUID bundleId : subscriptionsPerBundle.keySet()) {
-            final List<Entitlement> entitlements = getAllEntitlementsForBundle(subscriptionsPerBundle.get(bundleId), tenantContext);
-            result.addAll(entitlements);
-        }
-        return result;
-    }
 
-    private List<Entitlement> getAllEntitlementsForBundle(final List<SubscriptionBase> subscriptions, final TenantContext context) {
-        return Lists.transform(subscriptions,
-                               new Function<SubscriptionBase, Entitlement>() {
-                                   @Override
-                                   public Entitlement apply(final SubscriptionBase input) {
-                                       try {
-                                           return getEntitlementForId(input.getId(), context);
-                                       } catch (EntitlementApiException e) {
-                                           throw new RuntimeException("Failed to extract blocking state for subscription " + input.getId().toString());
-                                       }
-                                   }
-                               });
+        final AccountEventsStreams accountEventsStreams = eventsStreamBuilder.buildForAccount(context);
+        final List<EventsStream> eventsStreams = ImmutableList.<EventsStream>copyOf(Iterables.<EventsStream>concat(accountEventsStreams.getEventsStreams().values()));
+        return Lists.<EventsStream, Entitlement>transform(eventsStreams,
+                                                          new Function<EventsStream, Entitlement>() {
+                                                              @Override
+                                                              public Entitlement apply(final EventsStream eventsStream) {
+                                                                  return new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi,
+                                                                                                blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
+                                                                                                entitlementUtils, dateHelper, clock, internalCallContextFactory);
+                                                              }
+                                                          });
     }
 
     @Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscription.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscription.java
index 25842b7..3938659 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscription.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscription.java
@@ -16,29 +16,32 @@
 
 package com.ning.billing.entitlement.api;
 
-import java.util.List;
+import java.util.Collection;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
 public class DefaultSubscription extends DefaultEntitlement implements Subscription {
 
-    private final List<BlockingState> blockingStates;
+    private final Collection<BlockingState> currentSubscriptionBlockingStatesForServices;
 
-    DefaultSubscription(final DefaultEntitlement entitlement, final List<BlockingState> blockingStates) {
+    DefaultSubscription(final DefaultEntitlement entitlement) {
         super(entitlement);
-        this.blockingStates = blockingStates;
+        this.currentSubscriptionBlockingStatesForServices = eventsStream.getCurrentSubscriptionEntitlementBlockingStatesForServices();
     }
 
     @Override
     public LocalDate getBillingStartDate() {
-        return new LocalDate(getSubscriptionBase().getStartDate(), getAccount().getTimeZone());
+        return new LocalDate(getSubscriptionBase().getStartDate(), getAccountTimeZone());
     }
 
     @Override
     public LocalDate getBillingEndDate() {
         final DateTime futureOrCurrentEndDateForSubscription = getSubscriptionBase().getEndDate() != null ? getSubscriptionBase().getEndDate() : getSubscriptionBase().getFutureEndDate();
-        final DateTime futureOrCurrentEndDateForBaseSubscription = getEventsStream().getBaseSubscription().getEndDate() != null ? getEventsStream().getBaseSubscription().getEndDate() : getEventsStream().getBaseSubscription().getFutureEndDate();
+        final DateTime futureOrCurrentEndDateForBaseSubscription = getBaseSubscription().getEndDate() != null ? getBaseSubscription().getEndDate() : getBaseSubscription().getFutureEndDate();
 
         final DateTime futureOrCurrentEndDate;
         if (futureOrCurrentEndDateForBaseSubscription != null && futureOrCurrentEndDateForBaseSubscription.isBefore(futureOrCurrentEndDateForSubscription)) {
@@ -47,25 +50,27 @@ public class DefaultSubscription extends DefaultEntitlement implements Subscript
             futureOrCurrentEndDate = futureOrCurrentEndDateForSubscription;
         }
 
-        return futureOrCurrentEndDate != null ? new LocalDate(futureOrCurrentEndDate, getAccount().getTimeZone()) : null;
+        return futureOrCurrentEndDate != null ? new LocalDate(futureOrCurrentEndDate, getAccountTimeZone()) : null;
     }
 
     @Override
     public LocalDate getChargedThroughDate() {
-        return getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), getAccount().getTimeZone()) : null;
+        return getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), getAccountTimeZone()) : null;
     }
 
     @Override
     public String getCurrentStateForService(final String serviceName) {
-
-        if (blockingStates == null) {
+        if (currentSubscriptionBlockingStatesForServices == null) {
             return null;
+        } else {
+            final BlockingState blockingState = Iterables.<BlockingState>tryFind(currentSubscriptionBlockingStatesForServices,
+                                                                                 new Predicate<BlockingState>() {
+                                                                                     @Override
+                                                                                     public boolean apply(final BlockingState input) {
+                                                                                         return serviceName.equals(input.getService());
+                                                                                     }
+                                                                                 }).orNull();
+            return blockingState == null ? null : blockingState.getService();
         }
-        for (BlockingState cur : blockingStates) {
-            if (cur.getService().equals(serviceName)) {
-                return cur.getStateName();
-            }
-        }
-        return null;
     }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
index 850317f..253525e 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -17,11 +17,11 @@
 package com.ning.billing.entitlement.api;
 
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.inject.Inject;
@@ -29,217 +29,204 @@ import javax.inject.Inject;
 import org.joda.time.DateTimeZone;
 
 import com.ning.billing.ErrorCode;
-import com.ning.billing.account.api.Account;
-import com.ning.billing.account.api.AccountApiException;
-import com.ning.billing.account.api.AccountInternalApi;
+import com.ning.billing.ObjectType;
 import com.ning.billing.callcontext.InternalTenantContext;
-import com.ning.billing.clock.Clock;
-import com.ning.billing.entitlement.block.BlockingChecker;
-import com.ning.billing.entitlement.dao.BlockingStateDao;
+import com.ning.billing.entitlement.AccountEntitlements;
+import com.ning.billing.entitlement.EntitlementInternalApi;
 import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
 import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+import com.ning.billing.util.cache.Cachable.CacheType;
+import com.ning.billing.util.cache.CacheControllerDispatcher;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.customfield.ShouldntHappenException;
+import com.ning.billing.util.dao.NonEntityDao;
 
-import com.google.common.base.Function;
+import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.LinkedListMultimap;
-import com.google.common.collect.ListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Ordering;
 
 public class DefaultSubscriptionApi implements SubscriptionApi {
 
+    private static final Comparator<SubscriptionBundle> SUBSCRIPTION_BUNDLE_COMPARATOR = new Comparator<SubscriptionBundle>() {
+        @Override
+        public int compare(final SubscriptionBundle o1, final SubscriptionBundle o2) {
+            final int compared = o1.getOriginalCreatedDate().compareTo(o2.getOriginalCreatedDate());
+            if (compared != 0) {
+                return compared;
+            } else {
+                final int compared2 = o1.getUpdatedDate().compareTo(o2.getUpdatedDate());
+                if (compared2 != 0) {
+                    return compared2;
+                } else {
+                    // Default, stable, ordering
+                    return o1.getId().compareTo(o2.getId());
+                }
+            }
+        }
+    };
+
+    private final EntitlementInternalApi entitlementInternalApi;
     private final SubscriptionBaseInternalApi subscriptionInternalApi;
-    private final EntitlementApi entitlementApi;
-    private final BlockingChecker checker;
-    private final BlockingStateDao blockingStateDao;
-    private final EntitlementDateHelper dateHelper;
-    private final Clock clock;
     private final InternalCallContextFactory internalCallContextFactory;
-    private final AccountInternalApi accountApi;
+    private final NonEntityDao nonEntityDao;
+    private final CacheControllerDispatcher cacheControllerDispatcher;
 
     @Inject
-    public DefaultSubscriptionApi(final SubscriptionBaseInternalApi subscriptionInternalApi, final EntitlementApi entitlementApi, final BlockingChecker checker, final BlockingStateDao blockingStateDao, final AccountInternalApi accountApi, final Clock clock, final InternalCallContextFactory internalCallContextFactory) {
+    public DefaultSubscriptionApi(final EntitlementInternalApi entitlementInternalApi, final SubscriptionBaseInternalApi subscriptionInternalApi,
+                                  final InternalCallContextFactory internalCallContextFactory, final NonEntityDao nonEntityDao, final CacheControllerDispatcher cacheControllerDispatcher) {
+        this.entitlementInternalApi = entitlementInternalApi;
         this.subscriptionInternalApi = subscriptionInternalApi;
-        this.entitlementApi = entitlementApi;
-        this.accountApi = accountApi;
-        this.checker = checker;
-        this.blockingStateDao = blockingStateDao;
-        this.dateHelper = new EntitlementDateHelper(accountApi, clock);
-        ;
-        this.clock = clock;
         this.internalCallContextFactory = internalCallContextFactory;
+        this.nonEntityDao = nonEntityDao;
+        this.cacheControllerDispatcher = cacheControllerDispatcher;
     }
 
     @Override
     public Subscription getSubscriptionForEntitlementId(final UUID entitlementId, final TenantContext context) throws SubscriptionApiException {
+        final Long accountRecordId = nonEntityDao.retrieveAccountRecordIdFromObject(entitlementId, ObjectType.SUBSCRIPTION, cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_RECORD_ID));
+        final UUID accountId = nonEntityDao.retrieveIdFromObject(accountRecordId, ObjectType.ACCOUNT);
+
+        // Retrieve entitlements
+        final AccountEntitlements accountEntitlements;
         try {
-            final Entitlement entitlement = entitlementApi.getEntitlementForId(entitlementId, context);
-            final InternalTenantContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(entitlement.getAccountId(), context);
-            return fromEntitlement(entitlement, contextWithValidAccountRecordId);
+            accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccountId(accountId, context);
         } catch (EntitlementApiException e) {
             throw new SubscriptionApiException(e);
         }
+
+        // Build subscriptions
+        final Iterable<Subscription> accountSubscriptions = Iterables.<Subscription>concat(buildSubscriptionsFromEntitlements(accountEntitlements).values());
+
+        return Iterables.<Subscription>find(accountSubscriptions,
+                                            new Predicate<Subscription>() {
+                                                @Override
+                                                public boolean apply(final Subscription subscription) {
+                                                    return subscription.getId().equals(entitlementId);
+                                                }
+                                            });
     }
 
     @Override
     public SubscriptionBundle getSubscriptionBundle(final UUID bundleId, final TenantContext context) throws SubscriptionApiException {
-
-        try {
-
-            final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForBundle(bundleId, context);
-            if (entitlements.isEmpty()) {
-                throw new SubscriptionApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_ID, bundleId);
-            }
-            return getSubscriptionBundleFromEntitlements(bundleId, entitlements, context);
-        } catch (EntitlementApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (SubscriptionBaseApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (AccountApiException e) {
-            throw new SubscriptionApiException(e);
+        final Long accountRecordId = nonEntityDao.retrieveAccountRecordIdFromObject(bundleId, ObjectType.BUNDLE, cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_RECORD_ID));
+        final UUID accountId = nonEntityDao.retrieveIdFromObject(accountRecordId, ObjectType.ACCOUNT);
+
+        final Optional<SubscriptionBundle> bundleOptional = Iterables.<SubscriptionBundle>tryFind(getSubscriptionBundlesForAccount(accountId, context),
+                                                                                                  new Predicate<SubscriptionBundle>() {
+                                                                                                      @Override
+                                                                                                      public boolean apply(final SubscriptionBundle bundle) {
+                                                                                                          return bundle.getId().equals(bundleId);
+                                                                                                      }
+                                                                                                  });
+        if (!bundleOptional.isPresent()) {
+            throw new SubscriptionApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_ID, bundleId);
+        } else {
+            return bundleOptional.get();
         }
     }
 
-
     @Override
     public List<SubscriptionBundle> getSubscriptionBundlesForAccountIdAndExternalKey(final UUID accountId, final String externalKey, final TenantContext context) throws SubscriptionApiException {
-
-        try {
-
-            final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForAccountIdAndExternalKey(accountId, externalKey, context);
-            if (entitlements.isEmpty()) {
-                throw new SubscriptionApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_KEY, externalKey);
-            }
-            return getSubscriptionBundles(entitlements, context);
-        } catch (EntitlementApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (SubscriptionBaseApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (AccountApiException e) {
-            throw new SubscriptionApiException(e);
-        }
+        return ImmutableList.<SubscriptionBundle>copyOf(Iterables.<SubscriptionBundle>filter(getSubscriptionBundlesForAccount(accountId, context),
+                                                                                             new Predicate<SubscriptionBundle>() {
+                                                                                                 @Override
+                                                                                                 public boolean apply(final SubscriptionBundle bundle) {
+                                                                                                     return bundle.getExternalKey().equals(externalKey);
+                                                                                                 }
+                                                                                             }));
     }
 
     @Override
-    public SubscriptionBundle getActiveSubscriptionBundleForExternalKey(final String externalKey,
-                                                                        final TenantContext context) throws SubscriptionApiException {
-
+    public SubscriptionBundle getActiveSubscriptionBundleForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
         final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
         try {
             final SubscriptionBaseBundle baseBundle = subscriptionInternalApi.getActiveBundleForKey(externalKey, internalContext);
-            final List<Entitlement> allEntitlementsForBundle = entitlementApi.getAllEntitlementsForBundle(baseBundle.getId(), context);
-
-            return getSubscriptionBundleFromEntitlements(baseBundle.getId(), allEntitlementsForBundle, context);
+            return getSubscriptionBundle(baseBundle.getId(), context);
         } catch (SubscriptionBaseApiException e) {
             throw new SubscriptionApiException(e);
-        } catch (EntitlementApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (AccountApiException e) {
-            throw new SubscriptionApiException(e);
         }
     }
 
     @Override
     public List<SubscriptionBundle> getSubscriptionBundlesForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
-
         final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
-        try {
-            final List<SubscriptionBaseBundle> baseBundles = subscriptionInternalApi.getBundlesForKey(externalKey, internalContext);
-            final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(baseBundles.size());
-            for (SubscriptionBaseBundle cur : baseBundles) {
-                final List<Entitlement> allEntitlementsForBundle = entitlementApi.getAllEntitlementsForBundle(cur.getId(), context);
-                final SubscriptionBundle bundle = getSubscriptionBundleFromEntitlements(cur.getId(), allEntitlementsForBundle, context);
-                result.add(bundle);
-            }
-            return result;
-        } catch (SubscriptionBaseApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (EntitlementApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (AccountApiException e) {
-            throw new SubscriptionApiException(e);
+        final List<SubscriptionBaseBundle> baseBundles = subscriptionInternalApi.getBundlesForKey(externalKey, internalContext);
+
+        final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(baseBundles.size());
+        for (final SubscriptionBaseBundle cur : baseBundles) {
+            final SubscriptionBundle bundle = getSubscriptionBundle(cur.getId(), context);
+            result.add(bundle);
         }
+
+        return result;
     }
 
     @Override
-    public List<SubscriptionBundle> getSubscriptionBundlesForAccountId(final UUID accountId,
-                                                                       final TenantContext context) throws SubscriptionApiException {
-        try {
+    public List<SubscriptionBundle> getSubscriptionBundlesForAccountId(final UUID accountId, final TenantContext context) throws SubscriptionApiException {
+        return getSubscriptionBundlesForAccount(accountId, context);
+    }
 
-            final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForAccountId(accountId, context);
-            if (entitlements.isEmpty()) {
-                return Collections.emptyList();
-            }
-            return getSubscriptionBundles(entitlements, context);
+    private List<SubscriptionBundle> getSubscriptionBundlesForAccount(final UUID accountId, final TenantContext tenantContext) throws SubscriptionApiException {
+        // Retrieve entitlements
+        final AccountEntitlements accountEntitlements;
+        try {
+            accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccountId(accountId, tenantContext);
         } catch (EntitlementApiException e) {
             throw new SubscriptionApiException(e);
-        } catch (SubscriptionBaseApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (AccountApiException e) {
-            throw new SubscriptionApiException(e);
-        }
-    }
-
-    private List<SubscriptionBundle> getSubscriptionBundles(final List<Entitlement> entitlements, final TenantContext context) throws SubscriptionBaseApiException, AccountApiException {
-        final ListMultimap<UUID, Entitlement> perBundleEntitlements = LinkedListMultimap.create();
-        for (Entitlement cur : entitlements) {
-            perBundleEntitlements.put(cur.getBundleId(), cur);
         }
 
-        final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(perBundleEntitlements.keySet().size());
-        for (UUID bundleId : perBundleEntitlements.keySet()) {
-            final List<Entitlement> allEntitlementsForBundle = perBundleEntitlements.get(bundleId);
-            final SubscriptionBundle bundle = getSubscriptionBundleFromEntitlements(bundleId, allEntitlementsForBundle, context);
-            result.add(bundle);
+        // Build subscriptions
+        final Map<UUID, List<Subscription>> subscriptionsPerBundle = buildSubscriptionsFromEntitlements(accountEntitlements);
+
+        final DateTimeZone accountTimeZone = accountEntitlements.getAccount().getTimeZone();
+
+        // Build subscription bundles
+        final List<SubscriptionBundle> bundles = new LinkedList<SubscriptionBundle>();
+        for (final UUID bundleId : subscriptionsPerBundle.keySet()) {
+            final List<Subscription> subscriptionsForBundle = subscriptionsPerBundle.get(bundleId);
+            final String externalKey = subscriptionsForBundle.get(0).getExternalKey();
+
+            final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone,
+                                                                                              accountId,
+                                                                                              bundleId,
+                                                                                              externalKey,
+                                                                                              accountEntitlements.getEntitlements().get(bundleId));
+
+            final SubscriptionBaseBundle baseBundle = accountEntitlements.getBundles().get(bundleId);
+            final SubscriptionBundle subscriptionBundle = new DefaultSubscriptionBundle(bundleId,
+                                                                                        accountId,
+                                                                                        externalKey,
+                                                                                        subscriptionsForBundle,
+                                                                                        timeline,
+                                                                                        baseBundle.getOriginalCreatedDate(),
+                                                                                        baseBundle.getCreatedDate(),
+                                                                                        baseBundle.getUpdatedDate());
+            bundles.add(subscriptionBundle);
         }
-        return result;
-    }
 
-    private Subscription fromEntitlement(final Entitlement entitlement, final InternalTenantContext internalTenantContext) {
-
-        final List<BlockingState> states = blockingStateDao.getBlockingState(entitlement.getId(), BlockingStateType.SUBSCRIPTION, internalTenantContext);
-        final Subscription result = new DefaultSubscription((DefaultEntitlement) entitlement, states);
-        return result;
+        // Sort the results for predictability
+        return Ordering.<SubscriptionBundle>from(SUBSCRIPTION_BUNDLE_COMPARATOR).sortedCopy(bundles);
     }
 
-    private SubscriptionBundle getSubscriptionBundleFromEntitlements(final UUID bundleId, final List<Entitlement> entitlements, final TenantContext context) throws SubscriptionBaseApiException, AccountApiException {
-        final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
-        final SubscriptionBaseBundle baseBundle = subscriptionInternalApi.getBundleFromId(bundleId, internalTenantContext);
-        final List<Subscription> subscriptions = new ArrayList<Subscription>(entitlements.size());
-        subscriptions.addAll(Collections2.transform(entitlements, new Function<Entitlement, Subscription>() {
-            @Override
-            public Subscription apply(final Entitlement input) {
-                return fromEntitlement(input, internalTenantContext);
+    private Map<UUID, List<Subscription>> buildSubscriptionsFromEntitlements(final AccountEntitlements accountEntitlements) {
+        final Map<UUID, List<Subscription>> subscriptionsPerBundle = new HashMap<UUID, List<Subscription>>();
+        for (final UUID bundleId : accountEntitlements.getEntitlements().keySet()) {
+            if (subscriptionsPerBundle.get(bundleId) == null) {
+                subscriptionsPerBundle.put(bundleId, new LinkedList<Subscription>());
             }
-        }));
-
-        final Account account = accountApi.getAccountById(baseBundle.getAccountId(), internalTenantContext);
-
-        final InternalTenantContext internalTenantContextWithAccountRecordId = internalCallContextFactory.createInternalTenantContext(account.getId(), context);
 
-        final DateTimeZone accountTimeZone = account.getTimeZone();
-
-        final List<BlockingState> allBlockingStatesPerAccountRecordId = blockingStateDao.getBlockingAllForAccountRecordId(internalTenantContextWithAccountRecordId);
-        final Set<UUID> allEntitlementIds = new HashSet<UUID>(Collections2.<Entitlement, UUID>transform(entitlements, new Function<Entitlement, UUID>() {
-            @Override
-            public UUID apply(final Entitlement input) {
-                return input.getId();
-            }
-        }));
-
-        final List<BlockingState> filteredBlockingStates = new LinkedList<BlockingState>(Collections2.filter(allBlockingStatesPerAccountRecordId, new Predicate<BlockingState>() {
-            @Override
-            public boolean apply(final BlockingState input) {
-                return input.getType() == BlockingStateType.ACCOUNT ||
-                       (input.getType() == BlockingStateType.SUBSCRIPTION_BUNDLE && input.getBlockedId().equals(bundleId)) ||
-                       (input.getType() == BlockingStateType.SUBSCRIPTION && allEntitlementIds.contains(input.getBlockedId()));
+            for (final Entitlement entitlement : accountEntitlements.getEntitlements().get(bundleId)) {
+                if (entitlement instanceof DefaultEntitlement) {
+                    subscriptionsPerBundle.get(bundleId).add(new DefaultSubscription((DefaultEntitlement) entitlement));
+                } else {
+                    throw new ShouldntHappenException("Entitlement should be a DefaultEntitlement instance");
+                }
             }
-        }));
-
-        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, account.getId(), bundleId, baseBundle.getExternalKey(), entitlements, filteredBlockingStates);
-        final DefaultSubscriptionBundle bundle = new DefaultSubscriptionBundle(bundleId, baseBundle.getAccountId(), baseBundle.getExternalKey(), subscriptions, timeline, baseBundle.getOriginalCreatedDate() , baseBundle.getCreatedDate(), baseBundle.getUpdatedDate());
-        return bundle;
+        }
+        return subscriptionsPerBundle;
     }
 }
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 796eb06..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
@@ -21,18 +21,19 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+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;
 
@@ -56,13 +57,8 @@ import com.google.common.collect.ImmutableList;
 
 public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTimeline {
 
-
     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";
 
@@ -71,8 +67,18 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
     private final UUID bundleId;
     private final String externalKey;
 
+    public DefaultSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final Collection<Entitlement> entitlements) {
+        final Collection<BlockingState> blockingStates = new HashSet<BlockingState>();
+        for (final Entitlement entitlement : entitlements) {
+            blockingStates.addAll(((DefaultEntitlement) entitlement).getEventsStream().getBlockingStates());
+        }
+        this.accountId = accountId;
+        this.bundleId = bundleId;
+        this.externalKey = externalKey;
+        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;
@@ -85,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 List<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
@@ -131,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.
@@ -163,20 +189,20 @@ 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);
 
             int currentIndex = i;
             if (shouldSwap) {
-                Collections.swap(events, i, i+1);
+                Collections.swap(events, i, i + 1);
             }
             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);
                     }
@@ -189,23 +215,19 @@ 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 &&
                 ((isAscending &&
                   ((idComp > 0) ||
                    (idComp == 0 && cur.getSubscriptionEventType().ordinal() > other.getSubscriptionEventType().ordinal()))) ||
-                (!isAscending &&
-                   ((idComp < 0) ||
-                    (idComp == 0 && cur.getSubscriptionEventType().ordinal() < other.getSubscriptionEventType().ordinal())))));
+                 (!isAscending &&
+                  ((idComp < 0) ||
+                   (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) {
@@ -226,11 +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());
         }
 
@@ -242,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) {
@@ -277,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));
             }
         }
@@ -292,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;
         }
 
@@ -325,7 +343,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
     }
 
     // Compute the initial stream of events based on the subscription base events
-    private LinkedList<SubscriptionEvent> computeSubscriptionBaseEvents(final List<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
+    private LinkedList<SubscriptionEvent> computeSubscriptionBaseEvents(final Collection<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
         final LinkedList<SubscriptionEvent> result = new LinkedList<SubscriptionEvent>();
         for (final Entitlement cur : entitlements) {
             final SubscriptionBase base = ((DefaultEntitlement) cur).getSubscriptionBase();
@@ -338,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;
+                        }
                     }
                 }
             }
@@ -404,8 +393,74 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
         result.add(index, event);
     }
 
+    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();
+        }
 
-    private SubscriptionEvent toSubscriptionEvent(final SubscriptionEvent prev, final SubscriptionEvent next, final UUID entitlementId, final BlockingState in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
         return new DefaultSubscriptionEvent(in.getId(),
                                             entitlementId,
                                             in.getEffectiveDate(),
@@ -413,24 +468,22 @@ 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);
     }
 
-
     private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
         return new DefaultSubscriptionEvent(in.getId(),
                                             in.getSubscriptionId(),
@@ -488,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:
              */
@@ -526,415 +574,115 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
         return events;
     }
 
-//
-// Internal class to keep the state associated with each subscription
-//
-private final static class TargetState {
-
-    private boolean isEntitlementStarted;
-    private boolean isEntitlementStopped;
-    private boolean isBillingStarted;
-    private boolean isBillingStopped;
-    private Map<String, BlockingState> perServiceBlockingState;
-
-    public TargetState() {
-        this.isEntitlementStarted = false;
-        this.isEntitlementStopped = false;
-        this.isBillingStarted = false;
-        this.isBillingStopped = false;
-        this.perServiceBlockingState = new HashMap<String, BlockingState>();
-    }
-
-    public void setEntitlementStarted() {
-        isEntitlementStarted = true;
-    }
-
-    public void setEntitlementStopped() {
-        isEntitlementStopped = true;
-    }
-
-    public void setBillingStarted() {
-        isBillingStarted = true;
-    }
-
-    public void setBillingStopped() {
-        isBillingStopped = true;
-    }
-
-    public void addEntitlementEvent(final SubscriptionEvent e) {
-        final BlockingState converted = new DefaultBlockingState(e.getEntitlementId(), BlockingStateType.SUBSCRIPTION,
-                                                                 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
+    // Internal class to keep the state associated with each subscription
     //
-    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(),
-                                                                          bs.getStateName(),
-                                                                          bs.getService(),
-                                                                          bs.isBlockChange(),
-                                                                          (bs.isBlockEntitlement() && isEntitlementStarted && !isEntitlementStopped),
-                                                                          (bs.isBlockBilling() && isBillingStarted && !isBillingStopped),
-                                                                          bs.getEffectiveDate());
-
-        final List<SubscriptionEventType> result = new ArrayList<SubscriptionEventType>(4);
-        if (fixedBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED)) {
-            isEntitlementStopped = true;
-            result.add(SubscriptionEventType.STOP_ENTITLEMENT);
-            return result;
-        }
+    private static final class TargetState {
 
-        //
-        // We look at the effect of the incoming event for the specific service, and then recompute the state after so we can compare if anything has changed
-        // across all services
-        //
-        final BlockingAggregator stateBefore = getState();
-        perServiceBlockingState.put(fixedBlockingState.getService(), fixedBlockingState);
-        final BlockingAggregator stateAfter = getState();
+        private boolean isEntitlementStarted;
+        private boolean isEntitlementStopped;
+        private boolean isBillingStarted;
+        private boolean isBillingStopped;
+        private Map<String, BlockingState> perServiceBlockingState;
 
-        final boolean shouldResumeEntitlement = isEntitlementStarted && !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
-        if (shouldResumeEntitlement) {
-            result.add(SubscriptionEventType.RESUME_ENTITLEMENT);
-        }
-        final boolean shouldResumeBilling = isBillingStarted && !isBillingStopped && stateBefore.isBlockBilling() && !stateAfter.isBlockBilling();
-        if (shouldResumeBilling) {
-            result.add(SubscriptionEventType.RESUME_BILLING);
+        public TargetState() {
+            this.isEntitlementStarted = false;
+            this.isEntitlementStopped = false;
+            this.isBillingStarted = false;
+            this.isBillingStopped = false;
+            this.perServiceBlockingState = new HashMap<String, BlockingState>();
         }
 
-        final boolean shouldBlockEntitlement = isEntitlementStarted && !isEntitlementStopped && !stateBefore.isBlockEntitlement() && stateAfter.isBlockEntitlement();
-        if (shouldBlockEntitlement) {
-            result.add(SubscriptionEventType.PAUSE_ENTITLEMENT);
-        }
-        final boolean shouldBlockBilling = isBillingStarted && !isBillingStopped && !stateBefore.isBlockBilling() && stateAfter.isBlockBilling();
-        if (shouldBlockBilling) {
-            result.add(SubscriptionEventType.PAUSE_BILLING);
+        public void setEntitlementStarted() {
+            isEntitlementStarted = true;
         }
 
-        if (!shouldResumeEntitlement && !shouldBlockEntitlement && !shouldBlockEntitlement && !shouldBlockBilling && !fixedBlockingState.getService().equals(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME)) {
-            result.add(SubscriptionEventType.SERVICE_STATE_CHANGE);
+        public void setEntitlementStopped() {
+            isEntitlementStopped = true;
         }
-        return result;
-    }
 
-    private BlockingAggregator getState() {
-        final DefaultBlockingAggregator aggrBefore = new DefaultBlockingAggregator();
-        for (BlockingState cur : perServiceBlockingState.values()) {
-            aggrBefore.or(cur);
+        public void setBillingStarted() {
+            isBillingStarted = true;
         }
-        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;
-    }
+        public void setBillingStopped() {
+            isBillingStopped = true;
+        }
 
-    @Override
-    public Plan getNextPlan() {
-        return nextPlan;
-    }
+        public void addEntitlementEvent(final SubscriptionEvent e) {
+            final BlockingState converted = new DefaultBlockingState(e.getEntitlementId(), BlockingStateType.SUBSCRIPTION,
+                                                                     e.getServiceStateName(), e.getServiceName(), false, e.isBlockedEntitlement(), e.isBlockedBilling(),
+                                                                     ((DefaultSubscriptionEvent) e).getEffectiveDateTime());
+            perServiceBlockingState.put(converted.getService(), converted);
+        }
 
-    @Override
-    public PlanPhase getNextPhase() {
-        return nextPlanPhase;
-    }
+        //
+        // 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(),
+                                                                              bs.getStateName(),
+                                                                              bs.getService(),
+                                                                              bs.isBlockChange(),
+                                                                              (bs.isBlockEntitlement() && isEntitlementStarted && !isEntitlementStopped),
+                                                                              (bs.isBlockBilling() && isBillingStarted && !isBillingStopped),
+                                                                              bs.getEffectiveDate());
+
+            final List<SubscriptionEventType> result = new ArrayList<SubscriptionEventType>(4);
+            if (fixedBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED)) {
+                isEntitlementStopped = true;
+                result.add(SubscriptionEventType.STOP_ENTITLEMENT);
+                return result;
+            }
 
-    @Override
-    public PriceList getNextPriceList() {
-        return nextPriceList;
-    }
+            //
+            // We look at the effect of the incoming event for the specific service, and then recompute the state after so we can compare if anything has changed
+            // across all services
+            //
+            final BlockingAggregator stateBefore = getState();
+            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();
 
-    @Override
-    public BillingPeriod getNextBillingPeriod() {
-        return nextBillingPeriod;
-    }
+            final boolean shouldResumeEntitlement = isEntitlementStarted && !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
+            if (shouldResumeEntitlement) {
+                result.add(SubscriptionEventType.RESUME_ENTITLEMENT);
+            }
+            final boolean shouldResumeBilling = isBillingStarted && !isBillingStopped && stateBefore.isBlockBilling() && !stateAfter.isBlockBilling();
+            if (shouldResumeBilling) {
+                result.add(SubscriptionEventType.RESUME_BILLING);
+            }
 
-    public DateTime getCreatedDate() {
-        return createdDate;
-    }
+            final boolean shouldBlockEntitlement = isEntitlementStarted && !isEntitlementStopped && !stateBefore.isBlockEntitlement() && stateAfter.isBlockEntitlement();
+            if (shouldBlockEntitlement) {
+                result.add(SubscriptionEventType.PAUSE_ENTITLEMENT);
+            }
+            final boolean shouldBlockBilling = isBillingStarted && !isBillingStopped && !stateBefore.isBlockBilling() && stateAfter.isBlockBilling();
+            if (shouldBlockBilling) {
+                result.add(SubscriptionEventType.PAUSE_BILLING);
+            }
 
-    @Override
-    public boolean equals(final Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
+            if (!shouldResumeEntitlement && !shouldBlockEntitlement && !shouldBlockEntitlement && !shouldBlockBilling && !fixedBlockingState.getService().equals(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME)) {
+                result.add(SubscriptionEventType.SERVICE_STATE_CHANGE);
+            }
+            return result;
         }
 
-        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;
+        private BlockingAggregator getState() {
+            final DefaultBlockingAggregator aggrBefore = new DefaultBlockingAggregator();
+            for (final BlockingState cur : perServiceBlockingState.values()) {
+                aggrBefore.or(cur);
+            }
+            return aggrBefore;
         }
-
-        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/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEntitlements.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEntitlements.java
new file mode 100644
index 0000000..72898be
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEntitlements.java
@@ -0,0 +1,90 @@
+/*
+ * 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.svcs;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.entitlement.AccountEntitlements;
+import com.ning.billing.entitlement.AccountEventsStreams;
+import com.ning.billing.entitlement.api.Entitlement;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+
+public class DefaultAccountEntitlements implements AccountEntitlements {
+
+    private final AccountEventsStreams accountEventsStreams;
+    private final Map<UUID, Collection<Entitlement>> entitlements;
+
+    public DefaultAccountEntitlements(final AccountEventsStreams accountEventsStreams, final Map<UUID, Collection<Entitlement>> entitlements) {
+        this.accountEventsStreams = accountEventsStreams;
+        this.entitlements = entitlements;
+    }
+
+    @Override
+    public Account getAccount() {
+        return accountEventsStreams.getAccount();
+    }
+
+    @Override
+    public Map<UUID, SubscriptionBaseBundle> getBundles() {
+        return accountEventsStreams.getBundles();
+    }
+
+    @Override
+    public Map<UUID, Collection<Entitlement>> getEntitlements() {
+        return entitlements;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("DefaultAccountEntitlements{");
+        sb.append("accountEventsStreams=").append(accountEventsStreams);
+        sb.append(", entitlements=").append(entitlements);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final DefaultAccountEntitlements that = (DefaultAccountEntitlements) o;
+
+        if (accountEventsStreams != null ? !accountEventsStreams.equals(that.accountEventsStreams) : that.accountEventsStreams != null) {
+            return false;
+        }
+        if (entitlements != null ? !entitlements.equals(that.entitlements) : that.entitlements != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = accountEventsStreams != null ? accountEventsStreams.hashCode() : 0;
+        result = 31 * result + (entitlements != null ? entitlements.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEventsStreams.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEventsStreams.java
new file mode 100644
index 0000000..d05df80
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEventsStreams.java
@@ -0,0 +1,108 @@
+/*
+ * 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.svcs;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.entitlement.AccountEventsStreams;
+import com.ning.billing.entitlement.EventsStream;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class DefaultAccountEventsStreams implements AccountEventsStreams {
+
+    private final Account account;
+    private final Map<UUID, Collection<EventsStream>> eventsStreams;
+    private final Map<UUID, SubscriptionBaseBundle> bundles = new HashMap<UUID, SubscriptionBaseBundle>();
+
+    public DefaultAccountEventsStreams(final Account account,
+                                       final Iterable<SubscriptionBaseBundle> bundles,
+                                       final Map<UUID, Collection<EventsStream>> eventsStreams) {
+        this.account = account;
+        this.eventsStreams = eventsStreams;
+        for (final SubscriptionBaseBundle baseBundle : bundles) {
+            this.bundles.put(baseBundle.getId(), baseBundle);
+        }
+    }
+
+    public DefaultAccountEventsStreams(final Account account) {
+        this(account, ImmutableList.<SubscriptionBaseBundle>of(), ImmutableMap.<UUID, Collection<EventsStream>>of());
+    }
+
+    @Override
+    public Account getAccount() {
+        return account;
+    }
+
+    @Override
+    public Map<UUID, SubscriptionBaseBundle> getBundles() {
+        return bundles;
+    }
+
+    @Override
+    public Map<UUID, Collection<EventsStream>> getEventsStreams() {
+        return eventsStreams;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("DefaultAccountEventsStreams{");
+        sb.append("account=").append(account);
+        sb.append(", eventsStreams=").append(eventsStreams);
+        sb.append(", bundles=").append(bundles);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final DefaultAccountEventsStreams that = (DefaultAccountEventsStreams) o;
+
+        if (account != null ? !account.equals(that.account) : that.account != null) {
+            return false;
+        }
+        if (bundles != null ? !bundles.equals(that.bundles) : that.bundles != null) {
+            return false;
+        }
+        if (eventsStreams != null ? !eventsStreams.equals(that.eventsStreams) : that.eventsStreams != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = account != null ? account.hashCode() : 0;
+        result = 31 * result + (eventsStreams != null ? eventsStreams.hashCode() : 0);
+        result = 31 * result + (bundles != null ? bundles.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
new file mode 100644
index 0000000..f941e9b
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
@@ -0,0 +1,101 @@
+/*
+ * 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.svcs;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import com.ning.billing.account.api.AccountInternalApi;
+import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.AccountEntitlements;
+import com.ning.billing.entitlement.AccountEventsStreams;
+import com.ning.billing.entitlement.EntitlementInternalApi;
+import com.ning.billing.entitlement.EventsStream;
+import com.ning.billing.entitlement.api.DefaultEntitlement;
+import com.ning.billing.entitlement.api.Entitlement;
+import com.ning.billing.entitlement.api.EntitlementApi;
+import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.entitlement.api.EntitlementDateHelper;
+import com.ning.billing.entitlement.block.BlockingChecker;
+import com.ning.billing.entitlement.dao.BlockingStateDao;
+import com.ning.billing.entitlement.engine.core.EntitlementUtils;
+import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
+import com.ning.billing.notificationq.api.NotificationQueueService;
+import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.callcontext.TenantContext;
+
+public class DefaultEntitlementInternalApi implements EntitlementInternalApi {
+
+    private final EntitlementApi entitlementApi;
+    private final SubscriptionBaseInternalApi subscriptionInternalApi;
+    private final Clock clock;
+    private final InternalCallContextFactory internalCallContextFactory;
+    private final BlockingChecker checker;
+    private final BlockingStateDao blockingStateDao;
+    private final EntitlementDateHelper dateHelper;
+    private final EventsStreamBuilder eventsStreamBuilder;
+    private final EntitlementUtils entitlementUtils;
+    private final NotificationQueueService notificationQueueService;
+
+    @Inject
+    public DefaultEntitlementInternalApi(final EntitlementApi entitlementApi, final InternalCallContextFactory internalCallContextFactory,
+                                         final SubscriptionBaseInternalApi subscriptionInternalApi,
+                                         final AccountInternalApi accountApi, final BlockingStateDao blockingStateDao, final Clock clock,
+                                         final BlockingChecker checker, final NotificationQueueService notificationQueueService,
+                                         final EventsStreamBuilder eventsStreamBuilder, final EntitlementUtils entitlementUtils) {
+        this.entitlementApi = entitlementApi;
+        this.internalCallContextFactory = internalCallContextFactory;
+        this.subscriptionInternalApi = subscriptionInternalApi;
+        this.clock = clock;
+        this.checker = checker;
+        this.blockingStateDao = blockingStateDao;
+        this.notificationQueueService = notificationQueueService;
+        this.eventsStreamBuilder = eventsStreamBuilder;
+        this.entitlementUtils = entitlementUtils;
+        this.dateHelper = new EntitlementDateHelper(accountApi, clock);
+    }
+
+    @Override
+    public AccountEntitlements getAllEntitlementsForAccountId(final UUID accountId, final TenantContext tenantContext) throws EntitlementApiException {
+        final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
+
+        final AccountEventsStreams accountEventsStreams = eventsStreamBuilder.buildForAccount(context);
+
+        final Map<UUID, Collection<Entitlement>> entitlementsPerBundle = new HashMap<UUID, Collection<Entitlement>>();
+        for (final UUID bundleId : accountEventsStreams.getEventsStreams().keySet()) {
+            if (entitlementsPerBundle.get(bundleId) == null) {
+                entitlementsPerBundle.put(bundleId, new LinkedList<Entitlement>());
+            }
+
+            for (final EventsStream eventsStream : accountEventsStreams.getEventsStreams().get(bundleId)) {
+                final Entitlement entitlement = new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi,
+                                                                       blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
+                                                                       entitlementUtils, dateHelper, clock, internalCallContextFactory);
+                entitlementsPerBundle.get(bundleId).add(entitlement);
+            }
+        }
+
+        return new DefaultAccountEntitlements(accountEventsStreams, entitlementsPerBundle);
+    }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
index 22f7434..2ca84c0 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
@@ -55,8 +55,8 @@ public class DefaultInternalBlockingApi implements BlockingInternalApi {
     }
 
     @Override
-    public List<BlockingState> getBlockingAll(final UUID overdueableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
-        return dao.getBlockingAll(overdueableId, blockingStateType, context);
+    public List<BlockingState> getBlockingAllForAccount(final InternalTenantContext context) {
+        return dao.getBlockingAllForAccountRecordId(context);
     }
 
     @Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateDao.java
index aaa286b..3dd9cdd 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateDao.java
@@ -51,27 +51,6 @@ public interface BlockingStateDao extends EntityDao<BlockingStateModelDao, Block
     public List<BlockingState> getBlockingState(UUID blockableId, BlockingStateType blockingStateType, InternalTenantContext context);
 
     /**
-     * Returns the state history for that specific service
-     *
-     * @param blockableId       id of the blockable object
-     * @param blockingStateType blockable object type
-     * @param serviceName       name of the service
-     * @param context           call context
-     * @return list of blocking states for that blockable object and service
-     */
-    public List<BlockingState> getBlockingHistoryForService(UUID blockableId, BlockingStateType blockingStateType, String serviceName, InternalTenantContext context);
-
-    /**
-     * Return all the events (past and future) across all services
-     *
-     * @param blockableId       id of the blockable object
-     * @param blockingStateType blockable object type
-     * @param context           call context
-     * @return list of blocking states for that blockable object
-     */
-    public List<BlockingState> getBlockingAll(UUID blockableId, BlockingStateType blockingStateType, InternalTenantContext context);
-
-    /**
      * Return all events (past and future) across all services) for a given callcontext (account_record_id)
      *
      * @param context call context
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateModelDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateModelDao.java
index 7727fa5..7ad96b8 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateModelDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateModelDao.java
@@ -30,14 +30,14 @@ import com.ning.billing.util.entity.dao.EntityModelDao;
 
 public class BlockingStateModelDao extends EntityBase implements EntityModelDao<BlockingState>{
 
-    private final UUID blockableId;
-    private final BlockingStateType type;
-    private final String state;
-    private final String service;
-    private final Boolean blockChange;
-    private final Boolean blockEntitlement;
-    private final Boolean blockBilling;
-    private final DateTime effectiveDate;
+    private UUID blockableId;
+    private BlockingStateType type;
+    private String state;
+    private String service;
+    private Boolean blockChange;
+    private Boolean blockEntitlement;
+    private Boolean blockBilling;
+    private DateTime effectiveDate;
     private boolean isActive;
 
     public BlockingStateModelDao(final UUID id, final UUID blockableId, final BlockingStateType blockingStateType, final String state, final String service, final Boolean blockChange, final Boolean blockEntitlement,
@@ -95,6 +95,42 @@ public class BlockingStateModelDao extends EntityBase implements EntityModelDao<
         return effectiveDate;
     }
 
+    public void setBlockableId(final UUID blockableId) {
+        this.blockableId = blockableId;
+    }
+
+    public void setType(final BlockingStateType type) {
+        this.type = type;
+    }
+
+    public void setState(final String state) {
+        this.state = state;
+    }
+
+    public void setService(final String service) {
+        this.service = service;
+    }
+
+    public void setBlockChange(final Boolean blockChange) {
+        this.blockChange = blockChange;
+    }
+
+    public void setBlockEntitlement(final Boolean blockEntitlement) {
+        this.blockEntitlement = blockEntitlement;
+    }
+
+    public void setBlockBilling(final Boolean blockBilling) {
+        this.blockBilling = blockBilling;
+    }
+
+    public void setEffectiveDate(final DateTime effectiveDate) {
+        this.effectiveDate = effectiveDate;
+    }
+
+    public void setIsActive(final boolean isActive) {
+        this.isActive = isActive;
+    }
+
     // TODO required for jdbi binder
     public boolean getIsActive() {
         return isActive;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateSqlDao.java
index e95ebe9..c69758b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateSqlDao.java
@@ -57,23 +57,16 @@ public interface BlockingStateSqlDao extends EntitySqlDao<BlockingStateModelDao,
                                                                  @Bind("effectiveDate") Date effectiveDate,
                                                                  @BindBean final InternalTenantContext context);
 
-
     @SqlQuery
     public abstract List<BlockingStateModelDao> getBlockingHistoryForService(@Bind("blockableId") UUID blockableId,
                                                                              @Bind("service") String serviceName,
                                                                              @BindBean final InternalTenantContext context);
 
-    @SqlQuery
-    public abstract List<BlockingStateModelDao> getBlockingAll(@Bind("blockableId") UUID blockableId,
-                                                               @BindBean final InternalTenantContext context);
-
-
     @SqlUpdate
     @Audited(ChangeType.UPDATE)
     public void unactiveEvent(@Bind("id") String id,
                               @BindBean final InternalCallContext context);
 
-
     public class BlockingHistorySqlMapper extends MapperBase implements ResultSetMapper<BlockingStateModelDao> {
 
         @Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/DefaultBlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/DefaultBlockingStateDao.java
index 1292cbf..7f21843 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/DefaultBlockingStateDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/DefaultBlockingStateDao.java
@@ -111,44 +111,6 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
     }
 
     @Override
-    public List<BlockingState> getBlockingHistoryForService(final UUID blockableId, final BlockingStateType blockingStateType, final String serviceName, final InternalTenantContext context) {
-        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<BlockingState>>() {
-            @Override
-            public List<BlockingState> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                final BlockingStateSqlDao sqlDao = entitySqlDaoWrapperFactory.become(BlockingStateSqlDao.class);
-                final List<BlockingStateModelDao> models = sqlDao.getBlockingHistoryForService(blockableId, serviceName, context);
-                final Collection<BlockingStateModelDao> modelsFiltered = filterBlockingStates(models, blockingStateType);
-                return new ArrayList<BlockingState>(Collections2.transform(modelsFiltered,
-                                                                           new Function<BlockingStateModelDao, BlockingState>() {
-                                                                               @Override
-                                                                               public BlockingState apply(@Nullable final BlockingStateModelDao src) {
-                                                                                   return BlockingStateModelDao.toBlockingState(src);
-                                                                               }
-                                                                           }));
-            }
-        });
-    }
-
-    @Override
-    public List<BlockingState> getBlockingAll(final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
-        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<BlockingState>>() {
-            @Override
-            public List<BlockingState> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                final BlockingStateSqlDao sqlDao = entitySqlDaoWrapperFactory.become(BlockingStateSqlDao.class);
-                final List<BlockingStateModelDao> models = sqlDao.getBlockingAll(blockableId, context);
-                final Collection<BlockingStateModelDao> modelsFiltered = filterBlockingStates(models, blockingStateType);
-                return new ArrayList<BlockingState>(Collections2.transform(modelsFiltered,
-                                                                           new Function<BlockingStateModelDao, BlockingState>() {
-                                                                               @Override
-                                                                               public BlockingState apply(@Nullable final BlockingStateModelDao src) {
-                                                                                   return BlockingStateModelDao.toBlockingState(src);
-                                                                               }
-                                                                           }));
-            }
-        });
-    }
-
-    @Override
     public List<BlockingState> getBlockingAllForAccountRecordId(final InternalTenantContext context) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<BlockingState>>() {
             @Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java
new file mode 100644
index 0000000..4f71fba
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java
@@ -0,0 +1,101 @@
+/*
+ * 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.dao;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.skife.jdbi.v2.IDBI;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.EventsStream;
+import com.ning.billing.entitlement.api.BlockingState;
+import com.ning.billing.entitlement.api.BlockingStateType;
+import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+import com.ning.billing.util.cache.CacheControllerDispatcher;
+import com.ning.billing.util.dao.NonEntityDao;
+
+import com.google.common.collect.ImmutableList;
+
+public class OptimizedProxyBlockingStateDao extends ProxyBlockingStateDao {
+
+    public OptimizedProxyBlockingStateDao(final EventsStreamBuilder eventsStreamBuilder, final SubscriptionBaseInternalApi subscriptionBaseInternalApi,
+                                          final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
+                                          final NonEntityDao nonEntityDao) {
+        super(eventsStreamBuilder, subscriptionBaseInternalApi, dbi, clock, cacheControllerDispatcher, nonEntityDao);
+    }
+
+    /**
+     * Retrieve blocking states for a given subscription
+     * <p/>
+     * If the specified subscription is not an add-on, we already have the blocking states
+     * (they are all on disk) - we simply return them and there is nothing to do.
+     * Otherwise, for add-ons, we will need to compute the blocking states not on disk.
+     * <p/>
+     * This is a special method for EventsStreamBuilder to save some DAO calls.
+     *
+     * @param subscriptionBlockingStatesOnDisk
+     *                                  blocking states on disk for that subscription
+     * @param allBlockingStatesOnDiskForAccount
+     *                                  all blocking states on disk for that account
+     * @param account                   account associated with the subscription
+     * @param bundle                    bundle associated with the subscription
+     * @param baseSubscription          base subscription (ProductCategory.BASE) associated with that bundle
+     * @param subscription              subscription for which to build blocking states
+     * @param allSubscriptionsForBundle all subscriptions associated with that bundle
+     * @param context                   call context
+     * @return blocking states for that subscription
+     * @throws EntitlementApiException
+     */
+    public List<BlockingState> getBlockingHistory(final List<BlockingState> subscriptionBlockingStatesOnDisk,
+                                                  final List<BlockingState> allBlockingStatesOnDiskForAccount,
+                                                  final Account account,
+                                                  final SubscriptionBaseBundle bundle,
+                                                  @Nullable final SubscriptionBase baseSubscription,
+                                                  final SubscriptionBase subscription,
+                                                  final List<SubscriptionBase> allSubscriptionsForBundle,
+                                                  final InternalTenantContext context) throws EntitlementApiException {
+        // blockable id points to a subscription, but make sure it's an add-on
+        if (!ProductCategory.ADD_ON.equals(subscription.getCategory())) {
+            // blockable id points to a base or standalone subscription, there is nothing to do
+            return subscriptionBlockingStatesOnDisk;
+        }
+
+        // Find all base entitlements that we care about (for which we want to find future cancelled add-ons)
+        final Iterable<EventsStream> eventsStreams = ImmutableList.<EventsStream>of(eventsStreamBuilder.buildForEntitlement(allBlockingStatesOnDiskForAccount,
+                                                                                                                            account,
+                                                                                                                            bundle,
+                                                                                                                            baseSubscription,
+                                                                                                                            allSubscriptionsForBundle,
+                                                                                                                            context));
+
+        return addBlockingStatesNotOnDisk(subscription.getId(),
+                                          BlockingStateType.SUBSCRIPTION,
+                                          new LinkedList<BlockingState>(subscriptionBlockingStatesOnDisk),
+                                          ImmutableList.<SubscriptionBase>of(baseSubscription),
+                                          eventsStreams);
+    }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
index 13f3cd5..bef2260 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
@@ -18,8 +18,10 @@ package com.ning.billing.entitlement.dao;
 
 import java.util.Collection;
 import java.util.Comparator;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
@@ -36,22 +38,20 @@ import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.clock.Clock;
 import com.ning.billing.entitlement.EntitlementService;
+import com.ning.billing.entitlement.EventsStream;
 import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
 import com.ning.billing.entitlement.api.DefaultEntitlementApi;
-import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
 import com.ning.billing.entitlement.api.EntitlementApiException;
-import com.ning.billing.entitlement.engine.core.EventsStream;
 import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
 import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
-import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
 import com.ning.billing.util.cache.CacheControllerDispatcher;
+import com.ning.billing.util.customfield.ShouldntHappenException;
 import com.ning.billing.util.dao.NonEntityDao;
 import com.ning.billing.util.entity.Pagination;
 
 import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Ordering;
 
@@ -60,29 +60,125 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
 
     private static final Logger log = LoggerFactory.getLogger(ProxyBlockingStateDao.class);
 
-    public static final Ordering<BlockingState> BLOCKING_STATE_ORDERING = Ordering.<BlockingState>from(new Comparator<BlockingState>() {
+    // Ordering is critical here, especially for Junction
+    public static List<BlockingState> sortedCopy(final Iterable<BlockingState> blockingStates) {
+        final List<BlockingState> blockingStatesSomewhatSorted = BLOCKING_STATE_ORDERING_WITH_TIES_UNHANDLED.immutableSortedCopy(blockingStates);
+
+        final List<BlockingState> result = new LinkedList<BlockingState>();
+
+        // Take care of the ties
+        final Iterator<BlockingState> iterator = blockingStatesSomewhatSorted.iterator();
+        BlockingState prev = null;
+        while (iterator.hasNext()) {
+            final BlockingState current = iterator.next();
+            if (iterator.hasNext()) {
+                final BlockingState next = iterator.next();
+                if (prev != null && current.getEffectiveDate().equals(next.getEffectiveDate()) && current.getBlockedId().equals(next.getBlockedId())) {
+                    // Same date, same blockable id
+
+                    // Make sure block billing transitions are respected first
+                    BlockingState prevCandidate = insertTiedBlockingStatesInTheRightOrder(result, current, next, prev.isBlockBilling(), current.isBlockBilling(), next.isBlockBilling());
+                    if (prevCandidate == null) {
+                        // Then respect block entitlement transitions
+                        prevCandidate = insertTiedBlockingStatesInTheRightOrder(result, current, next, prev.isBlockEntitlement(), current.isBlockEntitlement(), next.isBlockEntitlement());
+                        if (prevCandidate == null) {
+                            // And finally block changes transitions
+                            prevCandidate = insertTiedBlockingStatesInTheRightOrder(result, current, next, prev.isBlockChange(), current.isBlockChange(), next.isBlockChange());
+                            if (prevCandidate == null) {
+                                // Trust the creation date (see BLOCKING_STATE_ORDERING_WITH_TIES_UNHANDLED below)
+                                result.add(current);
+                                result.add(next);
+                                prev = next;
+                            } else {
+                                prev = prevCandidate;
+                            }
+                        } else {
+                            prev = prevCandidate;
+                        }
+                    } else {
+                        prev = prevCandidate;
+                    }
+                } else {
+                    result.add(current);
+                    result.add(next);
+                    prev = next;
+                }
+            } else {
+                // End of the list
+                result.add(current);
+            }
+        }
+
+        return result;
+    }
+
+    private static BlockingState insertTiedBlockingStatesInTheRightOrder(final Collection<BlockingState> result,
+                                                                         final BlockingState current,
+                                                                         final BlockingState next,
+                                                                         final boolean prevBlocked,
+                                                                         final boolean currentBlocked,
+                                                                         final boolean nextBlocked) {
+        final BlockingState prev;
+
+        if (prevBlocked && currentBlocked && nextBlocked) {
+            // Tricky use case, bail
+            return null;
+        } else if (prevBlocked && currentBlocked && !nextBlocked) {
+            result.add(next);
+            result.add(current);
+            prev = current;
+        } else if (prevBlocked && !currentBlocked && nextBlocked) {
+            result.add(current);
+            result.add(next);
+            prev = next;
+        } else if (prevBlocked && !currentBlocked && !nextBlocked) {
+            // Tricky use case, bail
+            return null;
+        } else if (!prevBlocked && currentBlocked && nextBlocked) {
+            // Tricky use case, bail
+            return null;
+        } else if (!prevBlocked && currentBlocked && !nextBlocked) {
+            result.add(current);
+            result.add(next);
+            prev = next;
+        } else if (!prevBlocked && !currentBlocked && nextBlocked) {
+            result.add(next);
+            result.add(current);
+            prev = current;
+        } else if (!prevBlocked && !currentBlocked && !nextBlocked) {
+            // Tricky use case, bail
+            return null;
+        } else {
+            throw new ShouldntHappenException("Marker exception for code clarity");
+        }
+
+        return prev;
+    }
+
+    private static final Ordering<BlockingState> BLOCKING_STATE_ORDERING_WITH_TIES_UNHANDLED = Ordering.<BlockingState>from(new Comparator<BlockingState>() {
         @Override
         public int compare(final BlockingState o1, final BlockingState o2) {
-            final int blockableIdComparison = o1.getBlockedId().compareTo(o2.getBlockedId());
-            if (blockableIdComparison == 0) {
-                // effective_date column NOT NULL
-                final int comparison = o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
-                if (comparison == 0) {
-                    // Keep a stable ordering for ties
-                    return o1.getCreatedDate().compareTo(o2.getCreatedDate());
+            // effective_date column NOT NULL
+            final int effectiveDateComparison = o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
+            if (effectiveDateComparison != 0) {
+                return effectiveDateComparison;
+            } else {
+                final int blockableIdComparison = o1.getBlockedId().compareTo(o2.getBlockedId());
+                if (blockableIdComparison != 0) {
+                    return blockableIdComparison;
                 } else {
-                    return comparison;
+                    // Same date, same blockable id, just respect the created date for now (see sortedCopyOf method above)
+                    return o1.getCreatedDate().compareTo(o2.getCreatedDate());
                 }
-            } else {
-                return blockableIdComparison;
             }
         }
     });
 
-    private final EventsStreamBuilder eventsStreamBuilder;
     private final SubscriptionBaseInternalApi subscriptionInternalApi;
     private final Clock clock;
-    private final DefaultBlockingStateDao delegate;
+
+    protected final EventsStreamBuilder eventsStreamBuilder;
+    protected final DefaultBlockingStateDao delegate;
 
     @Inject
     public ProxyBlockingStateDao(final EventsStreamBuilder eventsStreamBuilder, final SubscriptionBaseInternalApi subscriptionBaseInternalApi,
@@ -145,21 +241,9 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
     }
 
     @Override
-    public List<BlockingState> getBlockingHistoryForService(final UUID blockableId, final BlockingStateType blockingStateType, final String serviceName, final InternalTenantContext context) {
-        final List<BlockingState> statesOnDisk = delegate.getBlockingHistoryForService(blockableId, blockingStateType, serviceName, context);
-        return addBlockingStatesNotOnDisk(blockableId, blockingStateType, statesOnDisk, context);
-    }
-
-    @Override
-    public List<BlockingState> getBlockingAll(final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
-        final List<BlockingState> statesOnDisk = delegate.getBlockingAll(blockableId, blockingStateType, context);
-        return addBlockingStatesNotOnDisk(blockableId, blockingStateType, statesOnDisk, context);
-    }
-
-    @Override
     public List<BlockingState> getBlockingAllForAccountRecordId(final InternalTenantContext context) {
         final List<BlockingState> statesOnDisk = delegate.getBlockingAllForAccountRecordId(context);
-        return addBlockingStatesNotOnDisk(null, null, statesOnDisk, context);
+        return addBlockingStatesNotOnDisk(statesOnDisk, context);
     }
 
     @Override
@@ -174,64 +258,37 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
 
     // Add blocking states for add-ons, which would be impacted by a future cancellation or change of their base plan
     // See DefaultEntitlement#blockAddOnsIfRequired
-    private List<BlockingState> addBlockingStatesNotOnDisk(@Nullable final UUID blockableId,
-                                                           @Nullable final BlockingStateType blockingStateType,
-                                                           final List<BlockingState> blockingStatesOnDisk,
+    private List<BlockingState> addBlockingStatesNotOnDisk(final List<BlockingState> blockingStatesOnDisk,
                                                            final InternalTenantContext context) {
         final Collection<BlockingState> blockingStatesOnDiskCopy = new LinkedList<BlockingState>(blockingStatesOnDisk);
 
         // Find all base entitlements that we care about (for which we want to find future cancelled add-ons)
         final Iterable<SubscriptionBase> baseSubscriptionsToConsider;
+        final Iterable<EventsStream> eventsStreams;
         try {
-            if (blockingStateType == null) {
-                // We're coming from getBlockingAllForAccountRecordId
-                final Iterable<SubscriptionBase> subscriptions = Iterables.<SubscriptionBase>concat(subscriptionInternalApi.getSubscriptionsForAccount(context).values());
-                baseSubscriptionsToConsider = Iterables.<SubscriptionBase>filter(subscriptions,
-                                                                                 new Predicate<SubscriptionBase>() {
-                                                                                     @Override
-                                                                                     public boolean apply(final SubscriptionBase input) {
-                                                                                         return ProductCategory.BASE.equals(input.getCategory()) &&
-                                                                                                !EntitlementState.CANCELLED.equals(input.getState());
-                                                                                     }
-                                                                                 });
-            } else if (BlockingStateType.SUBSCRIPTION.equals(blockingStateType)) {
-                // We're coming from getBlockingHistoryForService / getBlockingAll
-                final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(blockableId, context);
-
-                // blockable id points to a subscription, but make sure it's an add-on
-                if (ProductCategory.ADD_ON.equals(subscription.getCategory())) {
-                    final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(subscription.getBundleId(), context);
-                    baseSubscriptionsToConsider = ImmutableList.<SubscriptionBase>of(baseSubscription);
-                } else {
-                    // blockable id points to a base or standalone subscription, there is nothing to do
-                    return blockingStatesOnDisk;
-                }
-            } else {
-                // blockable id points to an account or bundle, in which case there are no extra blocking states to add
-                return blockingStatesOnDisk;
-            }
-        } catch (SubscriptionBaseApiException e) {
-            log.error("Error retrieving subscriptions for account record id " + context.getAccountRecordId(), e);
-            throw new RuntimeException(e);
-        }
-
-        // Retrieve the cancellation blocking state on disk, if it exists (will be used later)
-        final BlockingState cancellationBlockingStateOnDisk = findEntitlementCancellationBlockingState(blockableId, blockingStatesOnDiskCopy);
-
-        final List<EventsStream> eventsStreams;
-        try {
-            if (blockingStateType == null) {
-                // We're coming from getBlockingAllForAccountRecordId
-                eventsStreams = eventsStreamBuilder.buildForAccount(context);
-            } else {
-                // We're coming from getBlockingHistoryForService / getBlockingAll
-                eventsStreams = ImmutableList.<EventsStream>of(eventsStreamBuilder.buildForEntitlement(baseSubscriptionsToConsider.iterator().next().getId(), context));
-            }
+            final Map<UUID, List<SubscriptionBase>> subscriptions = subscriptionInternalApi.getSubscriptionsForAccount(context);
+            baseSubscriptionsToConsider = Iterables.<SubscriptionBase>filter(Iterables.<SubscriptionBase>concat(subscriptions.values()),
+                                                                             new Predicate<SubscriptionBase>() {
+                                                                                 @Override
+                                                                                 public boolean apply(final SubscriptionBase input) {
+                                                                                     return ProductCategory.BASE.equals(input.getCategory());
+                                                                                 }
+                                                                             });
+            eventsStreams = Iterables.<EventsStream>concat(eventsStreamBuilder.buildForAccount(subscriptions, context).getEventsStreams().values());
         } catch (EntitlementApiException e) {
             log.error("Error computing blocking states for addons for account record id " + context.getAccountRecordId(), e);
             throw new RuntimeException(e);
         }
 
+        return addBlockingStatesNotOnDisk(null, null, blockingStatesOnDiskCopy, baseSubscriptionsToConsider, eventsStreams);
+    }
+
+    // Special signature for OptimizedProxyBlockingStateDao
+    protected List<BlockingState> addBlockingStatesNotOnDisk(@Nullable final UUID blockableId,
+                                                             @Nullable final BlockingStateType blockingStateType,
+                                                             final Collection<BlockingState> blockingStatesOnDiskCopy,
+                                                             final Iterable<SubscriptionBase> baseSubscriptionsToConsider,
+                                                             final Iterable<EventsStream> eventsStreams) {
         // Compute the blocking states not on disk for all base subscriptions
         final DateTime now = clock.getUTCNow();
         for (final SubscriptionBase baseSubscription : baseSubscriptionsToConsider) {
@@ -243,20 +300,23 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
                                                                                }
                                                                            });
 
-            // First, check to see if the base entitlement is cancelled. If so, cancel the
+            // First, check to see if the base entitlement is cancelled
             final Collection<BlockingState> blockingStatesNotOnDisk = eventsStream.computeAddonsBlockingStatesForFutureSubscriptionBaseEvents();
 
             // Inject the extra blocking states into the stream if needed
             for (final BlockingState blockingState : blockingStatesNotOnDisk) {
                 // If this entitlement is actually already cancelled, add the cancellation event we computed
                 // only if it's prior to the blocking state on disk (e.g. add-on future cancelled but base plan cancelled earlier).
-                final boolean overrideCancellationBlockingStateOnDisk = cancellationBlockingStateOnDisk != null &&
-                                                                        isEntitlementCancellationBlockingState(blockingState) &&
-                                                                        blockingState.getEffectiveDate().isBefore(cancellationBlockingStateOnDisk.getEffectiveDate());
+                BlockingState cancellationBlockingStateOnDisk = null;
+                boolean overrideCancellationBlockingStateOnDisk = false;
+                if (isEntitlementCancellationBlockingState(blockingState)) {
+                    cancellationBlockingStateOnDisk = findEntitlementCancellationBlockingState(blockingState.getBlockedId(), blockingStatesOnDiskCopy);
+                    overrideCancellationBlockingStateOnDisk = cancellationBlockingStateOnDisk != null && blockingState.getEffectiveDate().isBefore(cancellationBlockingStateOnDisk.getEffectiveDate());
+                }
 
                 if ((
                             blockingStateType == null ||
-                            // In case we're coming from getBlockingHistoryForService / getBlockingAll, make sure we don't add
+                            // In case we're coming from OptimizedProxyBlockingStateDao, make sure we don't add
                             // blocking states for other add-ons on that base subscription
                             (BlockingStateType.SUBSCRIPTION.equals(blockingStateType) && blockingState.getBlockedId().equals(blockableId))
                     ) && (
@@ -273,7 +333,7 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
         }
 
         // Return the sorted list
-        return BLOCKING_STATE_ORDERING.immutableSortedCopy(blockingStatesOnDiskCopy);
+        return sortedCopy(blockingStatesOnDiskCopy);
     }
 
     private BlockingState findEntitlementCancellationBlockingState(@Nullable final UUID blockedId, final Iterable<BlockingState> blockingStatesOnDisk) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java
index 926464e..1dcf932 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java
@@ -16,16 +16,18 @@
 
 package com.ning.billing.entitlement.engine.core;
 
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 
 import com.ning.billing.ObjectType;
@@ -35,14 +37,16 @@ import com.ning.billing.account.api.AccountInternalApi;
 import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.clock.Clock;
-import com.ning.billing.entitlement.EntitlementService;
+import com.ning.billing.entitlement.AccountEventsStreams;
+import com.ning.billing.entitlement.EventsStream;
 import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
 import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.entitlement.api.svcs.DefaultAccountEventsStreams;
 import com.ning.billing.entitlement.block.BlockingChecker;
-import com.ning.billing.entitlement.block.BlockingChecker.BlockingAggregator;
-import com.ning.billing.entitlement.dao.BlockingStateDao;
 import com.ning.billing.entitlement.dao.DefaultBlockingStateDao;
+import com.ning.billing.entitlement.dao.OptimizedProxyBlockingStateDao;
+import com.ning.billing.entitlement.dao.ProxyBlockingStateDao;
 import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
@@ -52,43 +56,40 @@ import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.dao.NonEntityDao;
 
+import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
-import static com.ning.billing.entitlement.dao.ProxyBlockingStateDao.BLOCKING_STATE_ORDERING;
-
 @Singleton
 public class EventsStreamBuilder {
 
     private final AccountInternalApi accountInternalApi;
     private final SubscriptionBaseInternalApi subscriptionInternalApi;
     private final BlockingChecker checker;
-    private final BlockingStateDao blockingStateDao;
+    private final OptimizedProxyBlockingStateDao blockingStateDao;
     private final DefaultBlockingStateDao defaultBlockingStateDao;
     private final Clock clock;
-    private final NonEntityDao nonEntityDao;
     private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
     public EventsStreamBuilder(final AccountInternalApi accountInternalApi, final SubscriptionBaseInternalApi subscriptionInternalApi,
                                final BlockingChecker checker, final IDBI dbi, final Clock clock,
                                final CacheControllerDispatcher cacheControllerDispatcher,
-                               final BlockingStateDao blockingStateDao, final NonEntityDao nonEntityDao,
+                               final NonEntityDao nonEntityDao,
                                final InternalCallContextFactory internalCallContextFactory) {
         this.accountInternalApi = accountInternalApi;
         this.subscriptionInternalApi = subscriptionInternalApi;
         this.checker = checker;
         this.clock = clock;
-        this.blockingStateDao = blockingStateDao;
-        this.nonEntityDao = nonEntityDao;
         this.internalCallContextFactory = internalCallContextFactory;
 
         this.defaultBlockingStateDao = new DefaultBlockingStateDao(dbi, clock, cacheControllerDispatcher, nonEntityDao);
+        this.blockingStateDao = new OptimizedProxyBlockingStateDao(this, subscriptionInternalApi, dbi, clock, cacheControllerDispatcher, nonEntityDao);
     }
 
     public EventsStream refresh(final EventsStream eventsStream, final TenantContext tenantContext) throws EntitlementApiException {
-        return buildForEntitlement(eventsStream.getSubscription().getId(), tenantContext);
+        return buildForEntitlement(eventsStream.getEntitlementId(), tenantContext);
     }
 
     public EventsStream buildForBaseSubscription(final UUID bundleId, final TenantContext tenantContext) throws EntitlementApiException {
@@ -108,25 +109,29 @@ public class EventsStreamBuilder {
         return buildForEntitlement(entitlementId, internalTenantContext);
     }
 
-    public List<EventsStream> buildForAccount(final InternalTenantContext internalTenantContext) throws EntitlementApiException {
+    public AccountEventsStreams buildForAccount(final InternalTenantContext internalTenantContext) throws EntitlementApiException {
         // Retrieve the subscriptions (map bundle id -> subscriptions)
         final Map<UUID, List<SubscriptionBase>> subscriptions = subscriptionInternalApi.getSubscriptionsForAccount(internalTenantContext);
-        if (subscriptions.isEmpty()) {
-            // Bail early
-            return ImmutableList.<EventsStream>of();
-        }
+        return buildForAccount(subscriptions, internalTenantContext);
+    }
 
+    // Special signature for ProxyBlockingStateDao to save a DAO call
+    public AccountEventsStreams buildForAccount(final Map<UUID, List<SubscriptionBase>> subscriptions, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
         // Retrieve the account
-        final UUID accountId = nonEntityDao.retrieveIdFromObject(internalTenantContext.getAccountRecordId(), ObjectType.ACCOUNT);
         final Account account;
         try {
-            account = accountInternalApi.getAccountById(accountId, internalTenantContext);
+            account = accountInternalApi.getAccountByRecordId(internalTenantContext.getAccountRecordId(), internalTenantContext);
         } catch (AccountApiException e) {
             throw new EntitlementApiException(e);
         }
 
+        if (subscriptions.isEmpty()) {
+            // Bail early
+            return new DefaultAccountEventsStreams(account);
+        }
+
         // Retrieve the bundles
-        final List<SubscriptionBaseBundle> bundles = subscriptionInternalApi.getBundlesForAccount(accountId, internalTenantContext);
+        final List<SubscriptionBaseBundle> bundles = subscriptionInternalApi.getBundlesForAccount(account.getId(), internalTenantContext);
         // Map bundle id -> bundles
         final Map<UUID, SubscriptionBaseBundle> bundlesPerId = new HashMap<UUID, SubscriptionBaseBundle>();
         for (final SubscriptionBaseBundle bundle : bundles) {
@@ -134,20 +139,31 @@ public class EventsStreamBuilder {
         }
 
         // Retrieve the blocking states
-        final List<BlockingState> blockingStatesForAccount = BLOCKING_STATE_ORDERING.immutableSortedCopy(defaultBlockingStateDao.getBlockingAllForAccountRecordId(internalTenantContext));
-        // Copy fully the list (avoid lazy loading)
-        final List<BlockingState> accountEntitlementStates = ImmutableList.<BlockingState>copyOf(Iterables.<BlockingState>filter(blockingStatesForAccount,
-                                                                                                                                 new Predicate<BlockingState>() {
-                                                                                                                                     @Override
-                                                                                                                                     public boolean apply(final BlockingState input) {
-                                                                                                                                         return BlockingStateType.ACCOUNT.equals(input.getType()) &&
-                                                                                                                                                EntitlementService.ENTITLEMENT_SERVICE_NAME.equals(input.getService()) &&
-                                                                                                                                                accountId.equals(input.getBlockedId());
-                                                                                                                                     }
-                                                                                                                                 }));
+        final List<BlockingState> blockingStatesForAccount = defaultBlockingStateDao.getBlockingAllForAccountRecordId(internalTenantContext);
+
+        // Optimization: build lookup tables for blocking states states
+        final Collection<BlockingState> accountBlockingStates = new LinkedList<BlockingState>();
+        final Map<UUID, List<BlockingState>> blockingStatesPerSubscription = new HashMap<UUID, List<BlockingState>>();
+        final Map<UUID, List<BlockingState>> blockingStatesPerBundle = new HashMap<UUID, List<BlockingState>>();
+        for (final BlockingState blockingState : blockingStatesForAccount) {
+            if (BlockingStateType.SUBSCRIPTION.equals(blockingState.getType())) {
+                if (blockingStatesPerSubscription.get(blockingState.getBlockedId()) == null) {
+                    blockingStatesPerSubscription.put(blockingState.getBlockedId(), new LinkedList<BlockingState>());
+                }
+                blockingStatesPerSubscription.get(blockingState.getBlockedId()).add(blockingState);
+            } else if (BlockingStateType.SUBSCRIPTION_BUNDLE.equals(blockingState.getType())) {
+                if (blockingStatesPerBundle.get(blockingState.getBlockedId()) == null) {
+                    blockingStatesPerBundle.put(blockingState.getBlockedId(), new LinkedList<BlockingState>());
+                }
+                blockingStatesPerBundle.get(blockingState.getBlockedId()).add(blockingState);
+            } else if (BlockingStateType.ACCOUNT.equals(blockingState.getType()) &&
+                       account.getId().equals(blockingState.getBlockedId())) {
+                accountBlockingStates.add(blockingState);
+            }
+        }
 
         // Build the EventsStream objects
-        final List<EventsStream> results = new LinkedList<EventsStream>();
+        final Map<UUID, Collection<EventsStream>> entitlementsPerBundle = new HashMap<UUID, Collection<EventsStream>>();
         for (final UUID bundleId : subscriptions.keySet()) {
             final SubscriptionBaseBundle bundle = bundlesPerId.get(bundleId);
             final List<SubscriptionBase> allSubscriptionsForBundle = subscriptions.get(bundleId);
@@ -158,34 +174,46 @@ public class EventsStreamBuilder {
                                                                                                   return ProductCategory.BASE.equals(input.getLastActiveProduct().getCategory());
                                                                                               }
                                                                                           }).orNull();
-            // Copy fully the list (avoid lazy loading)
-            final List<BlockingState> bundleEntitlementStates = ImmutableList.<BlockingState>copyOf(Iterables.<BlockingState>filter(blockingStatesForAccount,
-                                                                                                                                    new Predicate<BlockingState>() {
-                                                                                                                                        @Override
-                                                                                                                                        public boolean apply(final BlockingState input) {
-                                                                                                                                            return BlockingStateType.SUBSCRIPTION_BUNDLE.equals(input.getType()) &&
-                                                                                                                                                   EntitlementService.ENTITLEMENT_SERVICE_NAME.equals(input.getService()) &&
-                                                                                                                                                   bundle.getId().equals(input.getBlockedId());
-                                                                                                                                        }
-                                                                                                                                    }));
-
-            for (final SubscriptionBase subscriptionBase : allSubscriptionsForBundle) {
-                // Copy fully the list (avoid lazy loading)
-                final List<BlockingState> subscriptionEntitlementStates = ImmutableList.<BlockingState>copyOf(Iterables.<BlockingState>filter(blockingStatesForAccount,
-                                                                                                                                              new Predicate<BlockingState>() {
-                                                                                                                                                  @Override
-                                                                                                                                                  public boolean apply(final BlockingState input) {
-                                                                                                                                                      return BlockingStateType.SUBSCRIPTION.equals(input.getType()) &&
-                                                                                                                                                             EntitlementService.ENTITLEMENT_SERVICE_NAME.equals(input.getService()) &&
-                                                                                                                                                             subscriptionBase.getId().equals(input.getBlockedId());
-                                                                                                                                                  }
-                                                                                                                                              }));
-
-                results.add(buildForEntitlement(account, bundle, baseSubscription, subscriptionBase, allSubscriptionsForBundle, subscriptionEntitlementStates, bundleEntitlementStates, accountEntitlementStates, internalTenantContext));
+            final List<BlockingState> bundleBlockingStates = Objects.firstNonNull(blockingStatesPerBundle.get(bundleId), ImmutableList.<BlockingState>of());
+
+            if (entitlementsPerBundle.get(bundleId) == null) {
+                entitlementsPerBundle.put(bundleId, new LinkedList<EventsStream>());
+            }
+
+            for (final SubscriptionBase subscription : allSubscriptionsForBundle) {
+                final List<BlockingState> subscriptionBlockingStatesOnDisk = Objects.firstNonNull(blockingStatesPerSubscription.get(subscription.getId()), ImmutableList.<BlockingState>of());
+
+                // We cannot always use blockingStatesForAccount here: we need subscriptionBlockingStates to contain the events not on disk when building an EventsStream
+                // for an add-on - which means going through the magic of ProxyBlockingStateDao, which will recursively
+                // create EventsStream objects. To avoid an infinite recursion, bypass ProxyBlockingStateDao when it's not
+                // needed, i.e. if this EventStream is for a standalone or a base subscription
+                final List<BlockingState> subscriptionBlockingStates;
+                if (baseSubscription == null || subscription.getId().equals(baseSubscription.getId())) {
+                    subscriptionBlockingStates = subscriptionBlockingStatesOnDisk;
+                } else {
+                    subscriptionBlockingStates = blockingStateDao.getBlockingHistory(subscriptionBlockingStatesOnDisk,
+                                                                                     blockingStatesForAccount,
+                                                                                     account,
+                                                                                     bundle,
+                                                                                     baseSubscription,
+                                                                                     subscription,
+                                                                                     allSubscriptionsForBundle,
+                                                                                     internalTenantContext);
+
+                }
+
+                // Merge the BlockingStates
+                final Collection<BlockingState> blockingStateSet = new LinkedHashSet<BlockingState>(accountBlockingStates);
+                blockingStateSet.addAll(bundleBlockingStates);
+                blockingStateSet.addAll(subscriptionBlockingStates);
+                final List<BlockingState> blockingStates = ProxyBlockingStateDao.sortedCopy(blockingStateSet);
+
+                final EventsStream eventStream = buildForEntitlement(account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, blockingStates, internalTenantContext);
+                entitlementsPerBundle.get(bundleId).add(eventStream);
             }
         }
 
-        return results;
+        return new DefaultAccountEventsStreams(account, bundles, entitlementsPerBundle);
     }
 
     public EventsStream buildForEntitlement(final UUID entitlementId, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
@@ -208,14 +236,6 @@ public class EventsStreamBuilder {
             throw new EntitlementApiException(e);
         }
 
-        return buildForEntitlement(bundle, baseSubscription, subscription, allSubscriptionsForBundle, internalTenantContext);
-    }
-
-    private EventsStream buildForEntitlement(final SubscriptionBaseBundle bundle,
-                                             final SubscriptionBase baseSubscription,
-                                             final SubscriptionBase subscription,
-                                             final List<SubscriptionBase> allSubscriptionsForBundle,
-                                             final InternalTenantContext internalTenantContext) throws EntitlementApiException {
         final Account account;
         try {
             account = accountInternalApi.getAccountById(bundle.getAccountId(), internalTenantContext);
@@ -223,58 +243,97 @@ public class EventsStreamBuilder {
             throw new EntitlementApiException(e);
         }
 
-        final List<BlockingState> bundleEntitlementStates = BLOCKING_STATE_ORDERING.immutableSortedCopy(defaultBlockingStateDao.getBlockingHistoryForService(bundle.getId(), BlockingStateType.SUBSCRIPTION_BUNDLE, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext));
-        final List<BlockingState> accountEntitlementStates = BLOCKING_STATE_ORDERING.immutableSortedCopy(defaultBlockingStateDao.getBlockingHistoryForService(account.getId(), BlockingStateType.ACCOUNT, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext));
-        // TODO PIERRE Explain the magic
-        final List<BlockingState> subscriptionEntitlementStates = subscription.getId().equals(baseSubscription.getId()) ?
-                                                                  BLOCKING_STATE_ORDERING.immutableSortedCopy(defaultBlockingStateDao.getBlockingHistoryForService(subscription.getId(), BlockingStateType.SUBSCRIPTION, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext)) :
-                                                                  BLOCKING_STATE_ORDERING.immutableSortedCopy(blockingStateDao.getBlockingHistoryForService(subscription.getId(), BlockingStateType.SUBSCRIPTION, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext));
+        // Retrieve the blocking states
+        final List<BlockingState> blockingStatesForAccount = defaultBlockingStateDao.getBlockingAllForAccountRecordId(internalTenantContext);
 
-        return buildForEntitlement(account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, subscriptionEntitlementStates, bundleEntitlementStates, accountEntitlementStates, internalTenantContext);
+        return buildForEntitlement(blockingStatesForAccount, account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, internalTenantContext);
     }
 
-    private EventsStream buildForEntitlement(final Account account,
+    // Special signature for OptimizedProxyBlockingStateDao to save some DAO calls
+    public EventsStream buildForEntitlement(final List<BlockingState> blockingStatesForAccount,
+                                            final Account account,
+                                            final SubscriptionBaseBundle bundle,
+                                            final SubscriptionBase baseSubscription,
+                                            final List<SubscriptionBase> allSubscriptionsForBundle,
+                                            final InternalTenantContext internalTenantContext) throws EntitlementApiException {
+        return buildForEntitlement(blockingStatesForAccount, account, bundle, baseSubscription, baseSubscription, allSubscriptionsForBundle, internalTenantContext);
+    }
+
+    private EventsStream buildForEntitlement(final List<BlockingState> blockingStatesForAccount,
+                                             final Account account,
                                              final SubscriptionBaseBundle bundle,
-                                             final SubscriptionBase baseSubscription,
+                                             @Nullable final SubscriptionBase baseSubscription,
                                              final SubscriptionBase subscription,
                                              final List<SubscriptionBase> allSubscriptionsForBundle,
-                                             final List<BlockingState> subscriptionEntitlementStates,
-                                             final List<BlockingState> bundleEntitlementStates,
-                                             final List<BlockingState> accountEntitlementStates,
                                              final InternalTenantContext internalTenantContext) throws EntitlementApiException {
-        final BlockingAggregator blockingAggregator = checker.getBlockedStatus(filterCurrentBlockableStatePerService(accountEntitlementStates),
-                                                                               filterCurrentBlockableStatePerService(bundleEntitlementStates),
-                                                                               filterCurrentBlockableStatePerService(subscriptionEntitlementStates),
-                                                                               internalTenantContext);
-
-        return new EventsStream(account,
-                                bundle,
-                                subscriptionEntitlementStates,
-                                bundleEntitlementStates,
-                                accountEntitlementStates,
-                                blockingAggregator,
-                                baseSubscription,
-                                subscription,
-                                allSubscriptionsForBundle,
-                                internalTenantContext,
-                                clock.getUTCNow());
-    }
-
-    private List<BlockingState> filterCurrentBlockableStatePerService(final Iterable<BlockingState> allBlockingStates) {
-        final DateTime now = clock.getUTCNow();
-
-        final Map<String, BlockingState> currentBlockingStatePerService = new HashMap<String, BlockingState>();
-        for (final BlockingState blockingState : allBlockingStates) {
-            if (blockingState.getEffectiveDate().isAfter(now)) {
-                continue;
+        // Optimization: build lookup tables for blocking states states
+        final Collection<BlockingState> accountBlockingStates = new LinkedList<BlockingState>();
+        final Map<UUID, List<BlockingState>> blockingStatesPerSubscription = new HashMap<UUID, List<BlockingState>>();
+        final Map<UUID, List<BlockingState>> blockingStatesPerBundle = new HashMap<UUID, List<BlockingState>>();
+        for (final BlockingState blockingState : blockingStatesForAccount) {
+            if (BlockingStateType.SUBSCRIPTION.equals(blockingState.getType())) {
+                if (blockingStatesPerSubscription.get(blockingState.getBlockedId()) == null) {
+                    blockingStatesPerSubscription.put(blockingState.getBlockedId(), new LinkedList<BlockingState>());
+                }
+                blockingStatesPerSubscription.get(blockingState.getBlockedId()).add(blockingState);
+            } else if (BlockingStateType.SUBSCRIPTION_BUNDLE.equals(blockingState.getType())) {
+                if (blockingStatesPerBundle.get(blockingState.getBlockedId()) == null) {
+                    blockingStatesPerBundle.put(blockingState.getBlockedId(), new LinkedList<BlockingState>());
+                }
+                blockingStatesPerBundle.get(blockingState.getBlockedId()).add(blockingState);
+            } else if (BlockingStateType.ACCOUNT.equals(blockingState.getType()) &&
+                       account.getId().equals(blockingState.getBlockedId())) {
+                accountBlockingStates.add(blockingState);
             }
+        }
 
-            if (currentBlockingStatePerService.get(blockingState.getService()) == null ||
-                currentBlockingStatePerService.get(blockingState.getService()).getEffectiveDate().isBefore(blockingState.getEffectiveDate())) {
-                currentBlockingStatePerService.put(blockingState.getService(), blockingState);
-            }
+        final List<BlockingState> bundleBlockingStates = Objects.firstNonNull(blockingStatesPerBundle.get(subscription.getBundleId()), ImmutableList.<BlockingState>of());
+        final List<BlockingState> subscriptionBlockingStatesOnDisk = Objects.firstNonNull(blockingStatesPerSubscription.get(subscription.getId()), ImmutableList.<BlockingState>of());
+
+        // We cannot always use blockingStatesForAccount here: we need subscriptionBlockingStates to contain the events not on disk when building an EventsStream
+        // for an add-on - which means going through the magic of ProxyBlockingStateDao, which will recursively
+        // create EventsStream objects. To avoid an infinite recursion, bypass ProxyBlockingStateDao when it's not
+        // needed, i.e. if this EventStream is for a standalone or a base subscription
+        final Collection<BlockingState> subscriptionBlockingStates;
+        if (baseSubscription == null || subscription.getId().equals(baseSubscription.getId())) {
+            // Note: we come here during the recursion from OptimizedProxyBlockingStateDao#getBlockingHistory
+            // (called by blockingStateDao.getBlockingHistory below)
+            subscriptionBlockingStates = subscriptionBlockingStatesOnDisk;
+        } else {
+            subscriptionBlockingStates = blockingStateDao.getBlockingHistory(ImmutableList.<BlockingState>copyOf(subscriptionBlockingStatesOnDisk),
+                                                                             blockingStatesForAccount,
+                                                                             account,
+                                                                             bundle,
+                                                                             baseSubscription,
+                                                                             subscription,
+                                                                             allSubscriptionsForBundle,
+                                                                             internalTenantContext);
         }
 
-        return ImmutableList.<BlockingState>copyOf(currentBlockingStatePerService.values());
+        // Merge the BlockingStates
+        final Collection<BlockingState> blockingStateSet = new LinkedHashSet<BlockingState>(accountBlockingStates);
+        blockingStateSet.addAll(bundleBlockingStates);
+        blockingStateSet.addAll(subscriptionBlockingStates);
+        final List<BlockingState> blockingStates = ProxyBlockingStateDao.sortedCopy(blockingStateSet);
+
+        return buildForEntitlement(account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, blockingStates, internalTenantContext);
+    }
+
+    private EventsStream buildForEntitlement(final Account account,
+                                             final SubscriptionBaseBundle bundle,
+                                             @Nullable final SubscriptionBase baseSubscription,
+                                             final SubscriptionBase subscription,
+                                             final List<SubscriptionBase> allSubscriptionsForBundle,
+                                             final List<BlockingState> blockingStates,
+                                             final InternalTenantContext internalTenantContext) throws EntitlementApiException {
+        return new DefaultEventsStream(account,
+                                       bundle,
+                                       blockingStates,
+                                       checker,
+                                       baseSubscription,
+                                       subscription,
+                                       allSubscriptionsForBundle,
+                                       internalTenantContext,
+                                       clock.getUTCNow());
     }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java b/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
index d95248b..975419d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
@@ -19,11 +19,13 @@ package com.ning.billing.entitlement.glue;
 import org.skife.config.ConfigSource;
 
 import com.ning.billing.entitlement.DefaultEntitlementService;
+import com.ning.billing.entitlement.EntitlementInternalApi;
 import com.ning.billing.entitlement.EntitlementService;
 import com.ning.billing.entitlement.api.DefaultEntitlementApi;
 import com.ning.billing.entitlement.api.DefaultSubscriptionApi;
 import com.ning.billing.entitlement.api.EntitlementApi;
 import com.ning.billing.entitlement.api.SubscriptionApi;
+import com.ning.billing.entitlement.api.svcs.DefaultEntitlementInternalApi;
 import com.ning.billing.entitlement.api.svcs.DefaultInternalBlockingApi;
 import com.ning.billing.entitlement.block.BlockingChecker;
 import com.ning.billing.entitlement.block.DefaultBlockingChecker;
@@ -38,7 +40,6 @@ import com.google.inject.AbstractModule;
 
 public class DefaultEntitlementModule extends AbstractModule implements EntitlementModule {
 
-
     public DefaultEntitlementModule(final ConfigSource configSource) {
     }
 
@@ -47,6 +48,7 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
         installBlockingStateDao();
         installBlockingApi();
         installEntitlementApi();
+        installEntitlementInternalApi();
         installSubscriptionApi();
         installBlockingChecker();
         bind(EntitlementService.class).to(DefaultEntitlementService.class).asEagerSingleton();
@@ -70,6 +72,11 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
     }
 
     @Override
+    public void installEntitlementInternalApi() {
+        bind(EntitlementInternalApi.class).to(DefaultEntitlementInternalApi.class).asEagerSingleton();
+    }
+
+    @Override
     public void installSubscriptionApi() {
         bind(SubscriptionApi.class).to(DefaultSubscriptionApi.class).asEagerSingleton();
     }
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
index 25067a4..62d8de4 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
@@ -5,6 +5,10 @@ tableName() ::= "blocking_states"
 
 andCheckSoftDeletionWithComma(prefix) ::= "and <prefix>is_active"
 
+defaultOrderBy(prefix) ::= <<
+order by <prefix>effective_date ASC, <recordIdField(prefix)> ASC
+>>
+
 tableFields(prefix) ::= <<
   <prefix>blockable_id
 , <prefix>type
@@ -50,7 +54,7 @@ and effective_date \<= :effectiveDate
 and is_active
 <AND_CHECK_TENANT()>
 -- We want the current state, hence the order desc and limit 1
-order by record_id desc
+order by effective_date desc, record_id desc
 limit 1
 ;
 >>
@@ -71,8 +75,8 @@ getBlockingState() ::= <<
          group by service
  ) tmp
  on t.record_id = tmp.record_id
- order by t.record_id asc
- ;
+ <defaultOrderBy("t.")>
+  ;
  >>
 
 getBlockingHistoryForService() ::= <<
@@ -84,19 +88,7 @@ where blockable_id = :blockableId
 and service = :service
 and is_active
 <AND_CHECK_TENANT()>
-order by record_id asc
-;
->>
-
-getBlockingAll() ::= <<
-select
-<allTableFields()>
-from
-<tableName()>
-where blockable_id = :blockableId
-and is_active
-<AND_CHECK_TENANT()>
-order by record_id asc
+<defaultOrderBy()>
 ;
 >>
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultEntitlementApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultEntitlementApi.java
index e2ff2d8..caa7178 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultEntitlementApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultEntitlementApi.java
@@ -62,15 +62,12 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         testListener.pushExpectedEvent(NextEvent.CREATE);
         final Entitlement addOnEntitlement = entitlementApi.addEntitlement(entitlement.getBundleId(), addOnSpec, initialDate, callContext);
         assertListenerStatus();
-
-        /*
-        // TODO It looks like we don't check if there is a future cancellation. Maybe we should?
         try {
             entitlement.uncancelEntitlement(callContext);
             Assert.fail("Entitlement hasn't been cancelled yet");
         } catch (final EntitlementApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.SUB_CANCEL_BAD_STATE.getCode());
-        }*/
+        }
 
         clock.addDays(3);
 
@@ -108,6 +105,40 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         }
     }
 
+
+    @Test(groups = "slow")
+    public void testUncancelEffectiveCancelledEntitlement() throws AccountApiException, EntitlementApiException {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        // Keep the same object for the whole test, to make sure we refresh its state before r/w calls
+        testListener.pushExpectedEvent(NextEvent.CREATE);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), initialDate, callContext);
+        assertListenerStatus();
+
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        clock.addDays(30);
+        assertListenerStatus();
+        subscriptionInternalApi.setChargedThroughDate(entitlement.getId(), clock.getUTCNow().plusMonths(1), internalCallContext);
+
+        final LocalDate entitlementCancelledDate = clock.getToday(account.getTimeZone());
+        testListener.pushExpectedEvent(NextEvent.BLOCK);
+        final Entitlement cancelledEntitlement = entitlement.cancelEntitlementWithDateOverrideBillingPolicy(clock.getToday(account.getTimeZone()), BillingActionPolicy.END_OF_TERM, callContext);
+        assertListenerStatus();
+        Assert.assertEquals(cancelledEntitlement.getEffectiveEndDate(), entitlementCancelledDate);
+
+        testListener.pushExpectedEvent(NextEvent.UNCANCEL);
+        cancelledEntitlement.uncancelEntitlement(callContext);
+        assertListenerStatus();
+
+        final Entitlement reactivatedEntitlement = entitlementApi.getEntitlementForId(cancelledEntitlement.getId(), callContext);
+        Assert.assertNull(reactivatedEntitlement.getEffectiveEndDate());
+    }
+
     @Test(groups = "slow")
     public void testCreateEntitlementWithCheck() throws AccountApiException, EntitlementApiException {
         final LocalDate initialDate = new LocalDate(2013, 8, 7);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionApi.java
index ea79a11..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
@@ -17,8 +17,10 @@
 package com.ning.billing.entitlement.api;
 
 import java.util.List;
+import java.util.UUID;
 
 import org.joda.time.LocalDate;
+import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
@@ -29,12 +31,56 @@ import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.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;
 
 public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbeddedDB {
 
+    @Test(groups = "slow", description = "Verify blocking states are exposed in SubscriptionBundle")
+    public void testBlockingStatesInTimelineApi() throws Exception {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement1 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), initialDate, callContext);
+        // Sleep 1 sec so created date are apparts from each other and ordering in the bundle does not default on the UUID which is random.
+        try {Thread.sleep(1000); } catch (InterruptedException ignore) {};
+        final Entitlement entitlement2 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), initialDate, callContext);
+        entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, "stateName", "service", false, false, false, clock.getUTCNow()),
+                                                                        internalCallContextFactory.createInternalCallContext(account.getId(), callContext));
+        assertListenerStatus();
+
+        final List<SubscriptionBundle> bundles = subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), callContext);
+        Assert.assertEquals(bundles.size(), 2);
+
+        // This will test the ordering as well
+        subscriptionBundleChecker(bundles, initialDate, entitlement1, 0);
+        subscriptionBundleChecker(bundles, initialDate, entitlement2, 1);
+    }
+
+    private void subscriptionBundleChecker(final List<SubscriptionBundle> bundles, final LocalDate initialDate, final Entitlement entitlement, final int idx) {
+        Assert.assertEquals(bundles.get(idx).getId(), entitlement.getBundleId());
+        Assert.assertEquals(bundles.get(idx).getSubscriptions().size(), 1);
+        Assert.assertEquals(bundles.get(idx).getSubscriptions().get(0).getId(), entitlement.getId());
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().size(), 4);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(0).getEffectiveDate(), initialDate);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(1).getEffectiveDate(), initialDate);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(2).getEffectiveDate(), initialDate);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(2).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(2).getServiceName(), "service");
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(2).getServiceStateName(), "stateName");
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(3).getEffectiveDate(), new LocalDate(2013, 9, 6));
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(3).getSubscriptionEventType(), SubscriptionEventType.PHASE);
+    }
 
     @Test(groups = "slow")
     public void testWithMultipleBundle() throws AccountApiException, SubscriptionApiException, EntitlementApiException {
@@ -132,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/block/TestBlockingApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/block/TestBlockingApi.java
index e227e1d..57073a4 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/block/TestBlockingApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/block/TestBlockingApi.java
@@ -23,11 +23,15 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.ning.billing.account.api.Account;
 import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
 import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
 import com.ning.billing.junction.DefaultBlockingState;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
@@ -77,6 +81,9 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
         final boolean blockEntitlement = false;
         final boolean blockBilling = false;
 
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), "TestBlockingApi", CallOrigin.TEST, UserType.SYSTEM, UUID.randomUUID());
+
         testListener.pushExpectedEvent(NextEvent.BLOCK);
         final BlockingState state1 = new DefaultBlockingState(uuid, BlockingStateType.ACCOUNT, overdueStateName, service, blockChange, blockEntitlement, blockBilling, clock.getUTCNow());
         blockingInternalApi.setBlockingState(state1, internalCallContext);
@@ -90,7 +97,7 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
         blockingInternalApi.setBlockingState(state2, internalCallContext);
         assertListenerStatus();
 
-        final List<BlockingState> blockingAll = blockingInternalApi.getBlockingAll(uuid, BlockingStateType.ACCOUNT, internalCallContext);
+        final List<BlockingState> blockingAll = blockingInternalApi.getBlockingAllForAccount(internalCallContext);
         final List<BlockingState> history = ImmutableList.<BlockingState>copyOf(Collections2.<BlockingState>filter(blockingAll,
                                                                                                                    new Predicate<BlockingState>() {
                                                                                                                        @Override
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java b/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
index f2bff3d..5f1faf6 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
@@ -32,6 +32,7 @@ import com.ning.billing.entitlement.api.BlockingStateType;
 import com.ning.billing.entitlement.api.EntitlementApiException;
 import com.ning.billing.util.entity.dao.MockEntityDaoBase;
 
+import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
@@ -39,12 +40,13 @@ import com.google.common.collect.ImmutableList;
 public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDao, BlockingState, EntitlementApiException> implements BlockingStateDao {
 
     private final Map<UUID, List<BlockingState>> blockingStates = new HashMap<UUID, List<BlockingState>>();
+    private final Map<Long, List<BlockingState>> blockingStatesPerAccountRecordId = new HashMap<Long, List<BlockingState>>();
 
-    // TODO This mock class should also check that events are past or present except for getBlockingAll
+    // TODO This mock class should also check that events are past or present
 
     @Override
     public BlockingState getBlockingStateForService(final UUID blockableId, final BlockingStateType blockingStateType, final String serviceName, final InternalTenantContext context) {
-        final List<BlockingState> states = getBlockingAll(blockableId, blockingStateType, context);
+        final List<BlockingState> states = blockingStates.get(blockableId);
         if (states == null) {
             return null;
         }
@@ -75,32 +77,8 @@ public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDa
     }
 
     @Override
-    public List<BlockingState> getBlockingHistoryForService(final UUID overdueableId, final BlockingStateType blockingStateType, final String serviceName, final InternalTenantContext context) {
-        final List<BlockingState> states = blockingStates.get(overdueableId);
-        if (states == null) {
-            return new ArrayList<BlockingState>();
-        }
-        final ImmutableList<BlockingState> filtered = ImmutableList.<BlockingState>copyOf(Collections2.filter(states, new Predicate<BlockingState>() {
-            @Override
-            public boolean apply(@Nullable final BlockingState input) {
-                return input.getService().equals(serviceName);
-            }
-        }));
-
-        // Note! The returned list cannot be immutable!
-        return states == null ? new ArrayList<BlockingState>() : new ArrayList<BlockingState>(filtered);
-    }
-
-    @Override
-    public List<BlockingState> getBlockingAll(final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
-        final List<BlockingState> states = blockingStates.get(blockableId);
-        // Note! The returned list cannot be immutable!
-        return states == null ? new ArrayList<BlockingState>() : states;
-    }
-
-    @Override
     public List<BlockingState> getBlockingAllForAccountRecordId(final InternalTenantContext context) {
-        throw new UnsupportedOperationException();
+        return Objects.firstNonNull(blockingStatesPerAccountRecordId.get(context.getAccountRecordId()), ImmutableList.<BlockingState>of());
     }
 
     @Override
@@ -109,6 +87,11 @@ public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDa
             blockingStates.put(state.getBlockedId(), new ArrayList<BlockingState>());
         }
         blockingStates.get(state.getBlockedId()).add(state);
+
+        if (blockingStatesPerAccountRecordId.get(context.getAccountRecordId()) == null) {
+            blockingStatesPerAccountRecordId.put(context.getAccountRecordId(), new ArrayList<BlockingState>());
+        }
+        blockingStatesPerAccountRecordId.get(context.getAccountRecordId()).add(state);
     }
 
     @Override
@@ -116,11 +99,8 @@ public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDa
         throw new UnsupportedOperationException();
     }
 
-    public synchronized void setBlockingStates(final UUID blockedId, final List<BlockingState> states) {
-        blockingStates.put(blockedId, states);
-    }
-
     public synchronized void clear() {
         blockingStates.clear();
+        blockingStatesPerAccountRecordId.clear();
     }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestBlockingDao.java b/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestBlockingDao.java
index db37ccd..264e810 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestBlockingDao.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestBlockingDao.java
@@ -21,8 +21,10 @@ import java.util.UUID;
 
 import org.joda.time.LocalDate;
 import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.ning.billing.account.api.Account;
 import com.ning.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
 import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
@@ -30,9 +32,16 @@ import com.ning.billing.junction.DefaultBlockingState;
 
 public class TestBlockingDao extends EntitlementTestSuiteWithEmbeddedDB {
 
-    @Test(groups = "slow")
-    public void testDao() {
+    @BeforeMethod(groups = "slow")
+    public void setUp() throws Exception {
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
 
+        // Override the context with the right account record id
+        internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+    }
+
+    @Test(groups = "slow", description = "Check BlockingStateDao with a single service")
+    public void testDaoWithOneService() {
         final UUID uuid = UUID.randomUUID();
         final String overdueStateName = "WayPassedItMan";
         final String service = "TEST";
@@ -54,15 +63,15 @@ public class TestBlockingDao extends EntitlementTestSuiteWithEmbeddedDB {
 
         Assert.assertEquals(blockingStateDao.getBlockingStateForService(uuid, BlockingStateType.ACCOUNT, service, internalCallContext).getStateName(), state2.getStateName());
 
-        final List<BlockingState> states = blockingStateDao.getBlockingHistoryForService(uuid, BlockingStateType.ACCOUNT, service, internalCallContext);
+        final List<BlockingState> states = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(states.size(), 2);
 
         Assert.assertEquals(states.get(0).getStateName(), overdueStateName);
         Assert.assertEquals(states.get(1).getStateName(), overdueStateName2);
     }
 
-    @Test(groups = "slow")
-    public void testDaoHistory() throws Exception {
+    @Test(groups = "slow", description = "Check BlockingStateDao with multiple services")
+    public void testDaoWithMultipleServices() throws Exception {
         final UUID uuid = UUID.randomUUID();
         final String overdueStateName = "WayPassedItMan";
         final String service1 = "TEST";
@@ -81,7 +90,7 @@ public class TestBlockingDao extends EntitlementTestSuiteWithEmbeddedDB {
         final BlockingState state2 = new DefaultBlockingState(uuid, BlockingStateType.ACCOUNT, overdueStateName2, service2, blockChange, blockEntitlement, blockBilling, clock.getUTCNow());
         blockingStateDao.setBlockingState(state2, clock, internalCallContext);
 
-        final List<BlockingState> history2 = blockingStateDao.getBlockingAll(uuid, BlockingStateType.ACCOUNT, internalCallContext);
+        final List<BlockingState> history2 = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(history2.size(), 2);
         Assert.assertEquals(history2.get(0).getStateName(), overdueStateName);
         Assert.assertEquals(history2.get(1).getStateName(), overdueStateName2);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java b/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java
index ed26f65..06989c9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java
@@ -22,6 +22,7 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
@@ -38,12 +39,21 @@ import com.ning.billing.junction.DefaultBlockingState;
 
 public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbeddedDB {
 
+    private Account account;
+
+    @BeforeMethod(groups = "slow")
+    public void setUp() throws Exception {
+        account = accountApi.createAccount(getAccountData(7), callContext);
+
+        // Override the context with the right account record id
+        internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+    }
+
     @Test(groups = "slow", description = "Verify we don't insert extra add-on events")
     public void testUnnecessaryEventsAreNotAdded() throws Exception {
         // This is a simple smoke test at the dao level only to make sure we do sane
         // things in case there are no future add-on cancellation events to add in the stream.
         // See TestEntitlementUtils for a more comprehensive test
-        final Account account = accountApi.createAccount(getAccountData(7), callContext);
         final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         testListener.pushExpectedEvent(NextEvent.CREATE);
         final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), clock.getUTCToday(), callContext);
@@ -54,14 +64,14 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         final String service = "service";
 
         // Verify initial state
-        Assert.assertEquals(blockingStateDao.getBlockingAll(entitlement.getId(), type, internalCallContext).size(), 0);
+        Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 0);
 
         // Set a state
         final DateTime stateDateTime = new DateTime(2013, 5, 6, 10, 11, 12, DateTimeZone.UTC);
         final BlockingState blockingState = new DefaultBlockingState(entitlement.getId(), type, state, service, false, false, false, stateDateTime);
         blockingStateDao.setBlockingState(blockingState, clock, internalCallContext);
 
-        Assert.assertEquals(blockingStateDao.getBlockingAll(entitlement.getId(), type, internalCallContext).size(), 1);
+        Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 1);
     }
 
     // See https://github.com/killbill/killbill/issues/111
@@ -75,7 +85,7 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         final String serviceB = "service-B";
 
         // Verify initial state
-        Assert.assertEquals(blockingStateDao.getBlockingAll(blockableId, type, internalCallContext).size(), 0);
+        Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 0);
 
         // Note: the checkers below rely on record_id ordering, not effective date
 
@@ -83,7 +93,7 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         final DateTime stateDateTime = new DateTime(2013, 5, 6, 10, 11, 12, DateTimeZone.UTC);
         final BlockingState blockingState1 = new DefaultBlockingState(blockableId, type, state, serviceA, false, false, false, stateDateTime);
         blockingStateDao.setBlockingState(blockingState1, clock, internalCallContext);
-        final List<BlockingState> blockingStates1 = blockingStateDao.getBlockingAll(blockableId, type, internalCallContext);
+        final List<BlockingState> blockingStates1 = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(blockingStates1.size(), 1);
         Assert.assertEquals(blockingStates1.get(0).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates1.get(0).getStateName(), state);
@@ -92,7 +102,7 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
 
         // Set the same state again - no change
         blockingStateDao.setBlockingState(blockingState1, clock, internalCallContext);
-        final List<BlockingState> blockingStates2 = blockingStateDao.getBlockingAll(blockableId, type, internalCallContext);
+        final List<BlockingState> blockingStates2 = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(blockingStates2.size(), 1);
         Assert.assertEquals(blockingStates2.get(0).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates2.get(0).getStateName(), state);
@@ -102,7 +112,7 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         // Set the state for service B
         final BlockingState blockingState2 = new DefaultBlockingState(blockableId, type, state, serviceB, false, false, false, stateDateTime);
         blockingStateDao.setBlockingState(blockingState2, clock, internalCallContext);
-        final List<BlockingState> blockingStates3 = blockingStateDao.getBlockingAll(blockableId, type, internalCallContext);
+        final List<BlockingState> blockingStates3 = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(blockingStates3.size(), 2);
         Assert.assertEquals(blockingStates3.get(0).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates3.get(0).getStateName(), state);
@@ -117,7 +127,7 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         final DateTime stateDateTime2 = new DateTime(2013, 6, 6, 10, 11, 12, DateTimeZone.UTC);
         final BlockingState blockingState3 = new DefaultBlockingState(blockableId, type, state, serviceA, false, false, false, stateDateTime2);
         blockingStateDao.setBlockingState(blockingState3, clock, internalCallContext);
-        final List<BlockingState> blockingStates4 = blockingStateDao.getBlockingAll(blockableId, type, internalCallContext);
+        final List<BlockingState> blockingStates4 = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(blockingStates4.size(), 2);
         Assert.assertEquals(blockingStates4.get(0).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates4.get(0).getStateName(), state);
@@ -132,31 +142,31 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         final DateTime stateDateTime3 = new DateTime(2013, 2, 6, 10, 11, 12, DateTimeZone.UTC);
         final BlockingState blockingState4 = new DefaultBlockingState(blockableId, type, state, serviceA, false, false, false, stateDateTime3);
         blockingStateDao.setBlockingState(blockingState4, clock, internalCallContext);
-        final List<BlockingState> blockingStates5 = blockingStateDao.getBlockingAll(blockableId, type, internalCallContext);
+        final List<BlockingState> blockingStates5 = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(blockingStates5.size(), 2);
         Assert.assertEquals(blockingStates5.get(0).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates5.get(0).getStateName(), state);
-        Assert.assertEquals(blockingStates5.get(0).getService(), serviceB);
-        Assert.assertEquals(blockingStates5.get(0).getEffectiveDate(), stateDateTime);
+        Assert.assertEquals(blockingStates5.get(0).getService(), serviceA);
+        Assert.assertEquals(blockingStates5.get(0).getEffectiveDate(), stateDateTime3);
         Assert.assertEquals(blockingStates5.get(1).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates5.get(1).getStateName(), state);
-        Assert.assertEquals(blockingStates5.get(1).getService(), serviceA);
-        Assert.assertEquals(blockingStates5.get(1).getEffectiveDate(), stateDateTime3);
+        Assert.assertEquals(blockingStates5.get(1).getService(), serviceB);
+        Assert.assertEquals(blockingStates5.get(1).getEffectiveDate(), stateDateTime);
 
         // Set a new state for service A
         final DateTime state2DateTime = new DateTime(2013, 12, 6, 10, 11, 12, DateTimeZone.UTC);
         final BlockingState blockingState5 = new DefaultBlockingState(blockableId, type, state2, serviceA, false, false, false, state2DateTime);
         blockingStateDao.setBlockingState(blockingState5, clock, internalCallContext);
-        final List<BlockingState> blockingStates6 = blockingStateDao.getBlockingAll(blockableId, type, internalCallContext);
+        final List<BlockingState> blockingStates6 = blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext);
         Assert.assertEquals(blockingStates6.size(), 3);
         Assert.assertEquals(blockingStates6.get(0).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates6.get(0).getStateName(), state);
-        Assert.assertEquals(blockingStates6.get(0).getService(), serviceB);
-        Assert.assertEquals(blockingStates6.get(0).getEffectiveDate(), stateDateTime);
+        Assert.assertEquals(blockingStates6.get(0).getService(), serviceA);
+        Assert.assertEquals(blockingStates6.get(0).getEffectiveDate(), stateDateTime3);
         Assert.assertEquals(blockingStates6.get(1).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates6.get(1).getStateName(), state);
-        Assert.assertEquals(blockingStates6.get(1).getService(), serviceA);
-        Assert.assertEquals(blockingStates6.get(1).getEffectiveDate(), stateDateTime3);
+        Assert.assertEquals(blockingStates6.get(1).getService(), serviceB);
+        Assert.assertEquals(blockingStates6.get(1).getEffectiveDate(), stateDateTime);
         Assert.assertEquals(blockingStates6.get(2).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates6.get(2).getStateName(), state2);
         Assert.assertEquals(blockingStates6.get(2).getService(), serviceA);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
index a0c387c..034a147 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
@@ -18,6 +18,7 @@ package com.ning.billing.entitlement.engine.core;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.UUID;
 
 import javax.annotation.Nullable;
 
@@ -29,6 +30,7 @@ import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
 import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.BillingActionPolicy;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
@@ -36,6 +38,7 @@ import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.EntitlementService;
 import com.ning.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
+import com.ning.billing.entitlement.EventsStream;
 import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
 import com.ning.billing.entitlement.api.DefaultEntitlement;
@@ -46,6 +49,9 @@ import com.ning.billing.entitlement.api.EntitlementApiException;
 import com.ning.billing.entitlement.dao.BlockingStateSqlDao;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
 
@@ -66,6 +72,9 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
         clock.setDay(initialDate);
         account = accountApi.createAccount(getAccountData(7), callContext);
 
+        // Override the context with the right account record id
+        internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE);
 
         // Create base entitlement
@@ -272,7 +281,7 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
         // Verify the blocking states DAO adds events not on disk for the first add-on...
         checkBlockingStatesDAO(changedBaseEntitlement, addOnEntitlement, baseEffectiveCancellationOrChangeDate, false);
         // ...but not for the second one
-        final List<BlockingState> blockingStatesForSecondAddOn = blockingStateDao.getBlockingAll(secondAddOnEntitlement.getId(), BlockingStateType.SUBSCRIPTION, internalCallContext);
+        final List<BlockingState> blockingStatesForSecondAddOn = blockingStatesForBlockedId(secondAddOnEntitlement.getId());
         Assert.assertEquals(blockingStatesForSecondAddOn.size(), 0);
     }
 
@@ -354,7 +363,14 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
 
     // Test the "read" path
     private void checkFutureBlockingStatesToCancel(final DefaultEntitlement baseEntitlement, @Nullable final DefaultEntitlement addOnEntitlement, @Nullable final DateTime effectiveCancellationDateTime) throws EntitlementApiException {
-        final Collection<BlockingState> blockingStatesForCancellation = computeFutureBlockingStatesForAssociatedAddons(baseEntitlement);
+        final Collection<BlockingState> blockingStatesForCancellationViaEntitlement = computeFutureBlockingStatesForAssociatedAddonsViaEntitlement(baseEntitlement);
+        doCheckFutureBlockingStatesToCancel(addOnEntitlement, effectiveCancellationDateTime, blockingStatesForCancellationViaEntitlement);
+
+        final Collection<BlockingState> blockingStatesForCancellationViaAccount = computeFutureBlockingStatesForAssociatedAddonsViaAccount(baseEntitlement);
+        doCheckFutureBlockingStatesToCancel(addOnEntitlement, effectiveCancellationDateTime, blockingStatesForCancellationViaAccount);
+    }
+
+    private void doCheckFutureBlockingStatesToCancel(final DefaultEntitlement addOnEntitlement, final DateTime effectiveCancellationDateTime, final Collection<BlockingState> blockingStatesForCancellation) {
         if (addOnEntitlement == null || effectiveCancellationDateTime == null) {
             Assert.assertEquals(blockingStatesForCancellation.size(), 0);
         } else {
@@ -370,7 +386,14 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
 
     // Test the "write" path
     private void checkActualBlockingStatesToCancel(final DefaultEntitlement baseEntitlement, final DefaultEntitlement addOnEntitlement, @Nullable final DateTime effectiveCancellationDateTime, final boolean approximateDateCheck) throws EntitlementApiException {
-        final Collection<BlockingState> blockingStatesForCancellation = computeBlockingStatesForAssociatedAddons(baseEntitlement, Objects.firstNonNull(effectiveCancellationDateTime, initialDate.toDateTimeAtStartOfDay()));
+        final Collection<BlockingState> blockingStatesForCancellationViaEntitlement = computeBlockingStatesForAssociatedAddonsViaEntitlement(baseEntitlement, Objects.firstNonNull(effectiveCancellationDateTime, initialDate.toDateTimeAtStartOfDay()));
+        doCheckActualBlockingStatesToCancel(addOnEntitlement, effectiveCancellationDateTime, approximateDateCheck, blockingStatesForCancellationViaEntitlement);
+
+        final Collection<BlockingState> blockingStatesForCancellationViaAccount = computeBlockingStatesForAssociatedAddonsViaAccount(baseEntitlement, Objects.firstNonNull(effectiveCancellationDateTime, initialDate.toDateTimeAtStartOfDay()));
+        doCheckActualBlockingStatesToCancel(addOnEntitlement, effectiveCancellationDateTime, approximateDateCheck, blockingStatesForCancellationViaAccount);
+    }
+
+    private void doCheckActualBlockingStatesToCancel(final DefaultEntitlement addOnEntitlement, final DateTime effectiveCancellationDateTime, final boolean approximateDateCheck, final Collection<BlockingState> blockingStatesForCancellation) {
         if (effectiveCancellationDateTime == null) {
             Assert.assertEquals(blockingStatesForCancellation.size(), 0);
         } else {
@@ -396,7 +419,7 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
 
     // Test the DAO
     private void checkBlockingStatesDAO(final DefaultEntitlement baseEntitlement, final DefaultEntitlement addOnEntitlement, final LocalDate effectiveBaseCancellationDate, final LocalDate effectiveAddOnCancellationDate, final boolean isBaseCancelled) {
-        final List<BlockingState> blockingStatesForBaseEntitlement = blockingStateDao.getBlockingAll(baseEntitlement.getId(), BlockingStateType.SUBSCRIPTION, internalCallContext);
+        final List<BlockingState> blockingStatesForBaseEntitlement = blockingStatesForBlockedId(baseEntitlement.getId());
         Assert.assertEquals(blockingStatesForBaseEntitlement.size(), isBaseCancelled ? 1 : 0);
         if (isBaseCancelled) {
             Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getBlockedId(), baseEntitlement.getId());
@@ -406,7 +429,7 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
             Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);
         }
 
-        final List<BlockingState> blockingStatesForAddOn = blockingStateDao.getBlockingAll(addOnEntitlement.getId(), BlockingStateType.SUBSCRIPTION, internalCallContext);
+        final List<BlockingState> blockingStatesForAddOn = blockingStatesForBlockedId(addOnEntitlement.getId());
         Assert.assertEquals(blockingStatesForAddOn.size(), 1);
         Assert.assertEquals(blockingStatesForAddOn.get(0).getBlockedId(), addOnEntitlement.getId());
         Assert.assertEquals(blockingStatesForAddOn.get(0).getEffectiveDate().toLocalDate(), effectiveAddOnCancellationDate);
@@ -415,13 +438,47 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
         Assert.assertEquals(blockingStatesForAddOn.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);
     }
 
-    private Collection<BlockingState> computeFutureBlockingStatesForAssociatedAddons(final DefaultEntitlement baseEntitlement) throws EntitlementApiException {
+    private Collection<BlockingState> computeFutureBlockingStatesForAssociatedAddonsViaEntitlement(final DefaultEntitlement baseEntitlement) throws EntitlementApiException {
         final EventsStream eventsStream = eventsStreamBuilder.buildForEntitlement(baseEntitlement.getId(), callContext);
         return eventsStream.computeAddonsBlockingStatesForFutureSubscriptionBaseEvents();
     }
 
-    private Collection<BlockingState> computeBlockingStatesForAssociatedAddons(final DefaultEntitlement baseEntitlement, final DateTime effectiveDate) throws EntitlementApiException {
+    private Collection<BlockingState> computeFutureBlockingStatesForAssociatedAddonsViaAccount(final DefaultEntitlement baseEntitlement) throws EntitlementApiException {
+        final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(baseEntitlement.getAccountId(), callContext);
+        final EventsStream eventsStream = Iterables.<EventsStream>find(Iterables.<EventsStream>concat(eventsStreamBuilder.buildForAccount(context).getEventsStreams().values()),
+                                                                       new Predicate<EventsStream>() {
+                                                                           @Override
+                                                                           public boolean apply(final EventsStream input) {
+                                                                               return input.getSubscription().getId().equals(baseEntitlement.getId());
+                                                                           }
+                                                                       });
+        return eventsStream.computeAddonsBlockingStatesForFutureSubscriptionBaseEvents();
+    }
+
+    private Collection<BlockingState> computeBlockingStatesForAssociatedAddonsViaEntitlement(final DefaultEntitlement baseEntitlement, final DateTime effectiveDate) throws EntitlementApiException {
         final EventsStream eventsStream = eventsStreamBuilder.buildForEntitlement(baseEntitlement.getId(), callContext);
         return eventsStream.computeAddonsBlockingStatesForNextSubscriptionBaseEvent(effectiveDate);
     }
+
+    private Collection<BlockingState> computeBlockingStatesForAssociatedAddonsViaAccount(final DefaultEntitlement baseEntitlement, final DateTime effectiveDate) throws EntitlementApiException {
+        final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(baseEntitlement.getAccountId(), callContext);
+        final EventsStream eventsStream = Iterables.<EventsStream>find(Iterables.<EventsStream>concat(eventsStreamBuilder.buildForAccount(context).getEventsStreams().values()),
+                                                                       new Predicate<EventsStream>() {
+                                                                           @Override
+                                                                           public boolean apply(final EventsStream input) {
+                                                                               return input.getSubscription().getId().equals(baseEntitlement.getId());
+                                                                           }
+                                                                       });
+        return eventsStream.computeAddonsBlockingStatesForNextSubscriptionBaseEvent(effectiveDate);
+    }
+
+    private List<BlockingState> blockingStatesForBlockedId(final UUID blockedId) {
+        return ImmutableList.<BlockingState>copyOf(Iterables.<BlockingState>filter(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext),
+                                                                                   new Predicate<BlockingState>() {
+                                                                                       @Override
+                                                                                       public boolean apply(final BlockingState input) {
+                                                                                           return input.getBlockedId().equals(blockedId);
+                                                                                       }
+                                                                                   }));
+    }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java b/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
index 2227fd6..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,8 @@ 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;
 
@@ -106,6 +108,10 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
     protected EntitlementUtils entitlementUtils;
     @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 e26016f..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.3-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemModelDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemModelDao.java
index a7df12c..7aa6e6c 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemModelDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemModelDao.java
@@ -133,6 +133,58 @@ public class InvoiceItemModelDao extends EntityBase implements EntityModelDao<In
         return linkedItemId;
     }
 
+    public void setType(final InvoiceItemType type) {
+        this.type = type;
+    }
+
+    public void setInvoiceId(final UUID invoiceId) {
+        this.invoiceId = invoiceId;
+    }
+
+    public void setAccountId(final UUID accountId) {
+        this.accountId = accountId;
+    }
+
+    public void setBundleId(final UUID bundleId) {
+        this.bundleId = bundleId;
+    }
+
+    public void setSubscriptionId(final UUID subscriptionId) {
+        this.subscriptionId = subscriptionId;
+    }
+
+    public void setPlanName(final String planName) {
+        this.planName = planName;
+    }
+
+    public void setPhaseName(final String phaseName) {
+        this.phaseName = phaseName;
+    }
+
+    public void setStartDate(final LocalDate startDate) {
+        this.startDate = startDate;
+    }
+
+    public void setEndDate(final LocalDate endDate) {
+        this.endDate = endDate;
+    }
+
+    public void setAmount(final BigDecimal amount) {
+        this.amount = amount;
+    }
+
+    public void setRate(final BigDecimal rate) {
+        this.rate = rate;
+    }
+
+    public void setCurrency(final Currency currency) {
+        this.currency = currency;
+    }
+
+    public void setLinkedItemId(final UUID linkedItemId) {
+        this.linkedItemId = linkedItemId;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceModelDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceModelDao.java
index cf3956d..569959e 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceModelDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceModelDao.java
@@ -119,6 +119,38 @@ public class InvoiceModelDao extends EntityBase implements EntityModelDao<Invoic
         return migrated;
     }
 
+    public void setAccountId(final UUID accountId) {
+        this.accountId = accountId;
+    }
+
+    public void setInvoiceNumber(final Integer invoiceNumber) {
+        this.invoiceNumber = invoiceNumber;
+    }
+
+    public void setInvoiceDate(final LocalDate invoiceDate) {
+        this.invoiceDate = invoiceDate;
+    }
+
+    public void setTargetDate(final LocalDate targetDate) {
+        this.targetDate = targetDate;
+    }
+
+    public void setCurrency(final Currency currency) {
+        this.currency = currency;
+    }
+
+    public void setMigrated(final boolean migrated) {
+        this.migrated = migrated;
+    }
+
+    public void setInvoiceItems(final List<InvoiceItemModelDao> invoiceItems) {
+        this.invoiceItems = invoiceItems;
+    }
+
+    public void setInvoicePayments(final List<InvoicePaymentModelDao> invoicePayments) {
+        this.invoicePayments = invoicePayments;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentModelDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentModelDao.java
index 21dddd4..f36b39c 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentModelDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentModelDao.java
@@ -99,6 +99,42 @@ public class InvoicePaymentModelDao extends EntityBase implements EntityModelDao
         return linkedInvoicePaymentId;
     }
 
+    public void setType(final InvoicePaymentType type) {
+        this.type = type;
+    }
+
+    public void setInvoiceId(final UUID invoiceId) {
+        this.invoiceId = invoiceId;
+    }
+
+    public void setPaymentId(final UUID paymentId) {
+        this.paymentId = paymentId;
+    }
+
+    public void setPaymentDate(final DateTime paymentDate) {
+        this.paymentDate = paymentDate;
+    }
+
+    public void setAmount(final BigDecimal amount) {
+        this.amount = amount;
+    }
+
+    public void setCurrency(final Currency currency) {
+        this.currency = currency;
+    }
+
+    public void setProcessedCurrency(final Currency processedCurrency) {
+        this.processedCurrency = processedCurrency;
+    }
+
+    public void setPaymentCookieId(final UUID paymentCookieId) {
+        this.paymentCookieId = paymentCookieId;
+    }
+
+    public void setLinkedInvoicePaymentId(final UUID linkedInvoicePaymentId) {
+        this.linkedInvoicePaymentId = linkedInvoicePaymentId;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/invoice/src/main/java/com/ning/billing/invoice/generator/InvoiceDateUtils.java b/invoice/src/main/java/com/ning/billing/invoice/generator/InvoiceDateUtils.java
index 8a439b1..ff1591c 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/generator/InvoiceDateUtils.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/generator/InvoiceDateUtils.java
@@ -60,6 +60,7 @@ public class InvoiceDateUtils {
         return days.divide(daysInPeriod, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD);
     }
 
+
     public static BigDecimal calculateProRationBeforeFirstBillingPeriod(final LocalDate startDate, final LocalDate nextBillingCycleDate,
                                                                         final BillingPeriod billingPeriod) {
         final LocalDate previousBillingCycleDate = nextBillingCycleDate.plusMonths(-billingPeriod.getNumberOfMonths());
@@ -73,10 +74,129 @@ public class InvoiceDateUtils {
         return numberOfMonths / numberOfMonthsInPeriod;
     }
 
+    public static LocalDate calculateLastBillingCycleDateBefore(final LocalDate date, final LocalDate previousBillCycleDate,
+                                                                final int billingCycleDay, final BillingPeriod billingPeriod) {
+        LocalDate proposedDate = previousBillCycleDate;
+
+        int numberOfPeriods = 0;
+        while (!proposedDate.isAfter(date)) {
+            proposedDate = previousBillCycleDate.plusMonths(numberOfPeriods * billingPeriod.getNumberOfMonths());
+            numberOfPeriods += 1;
+        }
+
+        proposedDate = proposedDate.plusMonths(-billingPeriod.getNumberOfMonths());
+
+        if (proposedDate.dayOfMonth().get() < billingCycleDay) {
+            final int lastDayOfTheMonth = proposedDate.dayOfMonth().getMaximumValue();
+            if (lastDayOfTheMonth < billingCycleDay) {
+                proposedDate = new LocalDate(proposedDate.getYear(), proposedDate.getMonthOfYear(), lastDayOfTheMonth);
+            } else {
+                proposedDate = new LocalDate(proposedDate.getYear(), proposedDate.getMonthOfYear(), billingCycleDay);
+            }
+        }
+
+        if (proposedDate.isBefore(previousBillCycleDate)) {
+            // Make sure not to go too far in the past
+            return previousBillCycleDate;
+        } else {
+            return proposedDate;
+        }
+    }
+
+    public static LocalDate calculateEffectiveEndDate(final LocalDate billCycleDate, final LocalDate targetDate,
+                                                      final BillingPeriod billingPeriod) {
+        if (targetDate.isBefore(billCycleDate)) {
+            return billCycleDate;
+        }
+
+        final int numberOfMonthsInPeriod = billingPeriod.getNumberOfMonths();
+        int numberOfPeriods = 0;
+        LocalDate proposedDate = billCycleDate;
+
+        while (!proposedDate.isAfter(targetDate)) {
+            proposedDate = billCycleDate.plusMonths(numberOfPeriods * numberOfMonthsInPeriod);
+            numberOfPeriods += 1;
+        }
+
+        return proposedDate;
+    }
+
+    public static LocalDate calculateEffectiveEndDate(final LocalDate billCycleDate, final LocalDate targetDate,
+                                                      final LocalDate endDate, final BillingPeriod billingPeriod) {
+        if (targetDate.isBefore(endDate)) {
+            if (targetDate.isBefore(billCycleDate)) {
+                return billCycleDate;
+            }
+
+            final int numberOfMonthsInPeriod = billingPeriod.getNumberOfMonths();
+            int numberOfPeriods = 0;
+            LocalDate proposedDate = billCycleDate;
+
+            while (!proposedDate.isAfter(targetDate)) {
+                proposedDate = billCycleDate.plusMonths(numberOfPeriods * numberOfMonthsInPeriod);
+                numberOfPeriods += 1;
+            }
+
+            // the current period includes the target date
+            // check to see whether the end date truncates the period
+            if (endDate.isBefore(proposedDate)) {
+                return endDate;
+            } else {
+                return proposedDate;
+            }
+        } else {
+            return endDate;
+        }
+    }
+
+
     public static BigDecimal calculateProRationAfterLastBillingCycleDate(final LocalDate endDate, final LocalDate previousBillThroughDate,
                                                                          final BillingPeriod billingPeriod) {
         // Note: assumption is that previousBillThroughDate is correctly aligned with the billing cycle day
         final LocalDate nextBillThroughDate = previousBillThroughDate.plusMonths(billingPeriod.getNumberOfMonths());
         return calculateProrationBetweenDates(previousBillThroughDate, endDate, previousBillThroughDate, nextBillThroughDate);
     }
+
+ /*
+    public static LocalDate calculateBillingCycleDateOnOrAfter(final LocalDate date, final DateTimeZone accountTimeZone,
+                                                               final int billingCycleDayLocal) {
+        final DateTime tmp = date.toDateTimeAtStartOfDay(accountTimeZone);
+        final DateTime proposedDateTime = calculateBillingCycleDateOnOrAfter(tmp, billingCycleDayLocal);
+
+        return new LocalDate(proposedDateTime, accountTimeZone);
+    }
+
+    public static LocalDate calculateBillingCycleDateAfter(final LocalDate date, final DateTimeZone accountTimeZone,
+                                                           final int billingCycleDayLocal) {
+        final DateTime tmp = date.toDateTimeAtStartOfDay(accountTimeZone);
+        final DateTime proposedDateTime = calculateBillingCycleDateAfter(tmp, billingCycleDayLocal);
+
+        return new LocalDate(proposedDateTime, accountTimeZone);
+    }
+    */
+
+    public static LocalDate calculateBillingCycleDateOnOrAfter(final LocalDate date, final int billingCycleDayLocal) {
+        final int lastDayOfMonth = date.dayOfMonth().getMaximumValue();
+
+        final LocalDate fixedDate;
+        if (billingCycleDayLocal > lastDayOfMonth) {
+            fixedDate = new LocalDate(date.getYear(), date.getMonthOfYear(), lastDayOfMonth, date.getChronology());
+        } else {
+            fixedDate = new LocalDate(date.getYear(), date.getMonthOfYear(), billingCycleDayLocal, date.getChronology());
+        }
+
+        LocalDate proposedDate = fixedDate;
+        while (proposedDate.isBefore(date)) {
+            proposedDate = proposedDate.plusMonths(1);
+        }
+        return proposedDate;
+    }
+
+    public static LocalDate calculateBillingCycleDateAfter(final LocalDate date, final int billingCycleDayLocal) {
+        LocalDate proposedDate = calculateBillingCycleDateOnOrAfter(date, billingCycleDayLocal);
+        if (date.compareTo(proposedDate) == 0) {
+            proposedDate = proposedDate.plusMonths(1);
+        }
+        return proposedDate;
+    }
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
index 506278a..2c94cb9 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
@@ -169,11 +169,11 @@ public class InvoiceDispatcher {
 
             // Make sure to first set the BCD if needed then get the account object (to have the BCD set)
             final BillingEventSet billingEvents = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId, context);
-            final Account account = accountApi.getAccountById(accountId, context);
 
+            final Account account = accountApi.getAccountById(accountId, context);
             final DateAndTimeZoneContext dateAndTimeZoneContext = billingEvents.iterator().hasNext() ?
                                                                   new DateAndTimeZoneContext(billingEvents.iterator().next().getEffectiveDate(), account.getTimeZone(), clock) :
-                                                                  new DateAndTimeZoneContext(null, account.getTimeZone(), clock);
+                                                                  null;
 
 
             List<Invoice> invoices = new ArrayList<Invoice>();
@@ -189,8 +189,8 @@ public class InvoiceDispatcher {
 
             final Currency targetCurrency = account.getCurrency();
 
-            final LocalDate targetDate = dateAndTimeZoneContext.computeTargetDate(targetDateTime);
-            final Invoice invoice = generator.generateInvoice(accountId, billingEvents, invoices, targetDate, targetCurrency);
+            final LocalDate targetDate = dateAndTimeZoneContext != null ? dateAndTimeZoneContext.computeTargetDate(targetDateTime) : null;
+            final Invoice invoice = targetDate != null ? generator.generateInvoice(accountId, billingEvents, invoices, targetDate, targetCurrency) : null;
             if (invoice == null) {
                 log.info("Generated null invoice for accountId {} and targetDate {} (targetDateTime {})", new Object[]{accountId, targetDate, targetDateTime});
                 if (!dryRun) {
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InAdvanceBillingMode.java b/invoice/src/main/java/com/ning/billing/invoice/model/InAdvanceBillingMode.java
index b2725e2..e3b4cd3 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InAdvanceBillingMode.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InAdvanceBillingMode.java
@@ -27,8 +27,10 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.invoice.generator.BillingIntervalDetail;
 
+import static com.ning.billing.invoice.generator.InvoiceDateUtils.calculateBillingCycleDateOnOrAfter;
+import static com.ning.billing.invoice.generator.InvoiceDateUtils.calculateEffectiveEndDate;
+import static com.ning.billing.invoice.generator.InvoiceDateUtils.calculateLastBillingCycleDateBefore;
 import static com.ning.billing.invoice.generator.InvoiceDateUtils.calculateNumberOfWholeBillingPeriods;
 import static com.ning.billing.invoice.generator.InvoiceDateUtils.calculateProRationAfterLastBillingCycleDate;
 import static com.ning.billing.invoice.generator.InvoiceDateUtils.calculateProRationBeforeFirstBillingPeriod;
@@ -50,8 +52,7 @@ public class InAdvanceBillingMode implements BillingMode {
 
         final List<RecurringInvoiceItemData> results = new ArrayList<RecurringInvoiceItemData>();
 
-        final BillingIntervalDetail billingIntervalDetail = new BillingIntervalDetail(startDate, endDate, targetDate, billingCycleDayLocal, billingPeriod);
-
+        final LocalDate firstBillingCycleDate = calculateBillingCycleDateOnOrAfter(startDate, billingCycleDayLocal);
 
         // We are not billing for less than a day (we could...)
         if (endDate != null && endDate.equals(startDate)) {
@@ -61,7 +62,7 @@ public class InAdvanceBillingMode implements BillingMode {
         // If there is an endDate and that endDate is before our first coming firstBillingCycleDate, all we have to do
         // is to charge for that period
         //
-        if (endDate != null && !endDate.isAfter(billingIntervalDetail.getFirstBillingCycleDate())) {
+        if (endDate != null && !endDate.isAfter(firstBillingCycleDate)) {
             final BigDecimal leadingProRationPeriods = calculateProRationBeforeFirstBillingPeriod(startDate, endDate, billingPeriod);
             final RecurringInvoiceItemData itemData = new RecurringInvoiceItemData(startDate, endDate, leadingProRationPeriods);
             results.add(itemData);
@@ -73,11 +74,11 @@ public class InAdvanceBillingMode implements BillingMode {
         // i) The first firstBillingCycleDate is strictly after our start date AND
         // ii) The endDate is is not null and is strictly after our firstBillingCycleDate (previous check)
         //
-        if (billingIntervalDetail.getFirstBillingCycleDate().isAfter(startDate)) {
-            final BigDecimal leadingProRationPeriods = calculateProRationBeforeFirstBillingPeriod(startDate, billingIntervalDetail.getFirstBillingCycleDate(), billingPeriod);
+        if (firstBillingCycleDate.isAfter(startDate)) {
+            final BigDecimal leadingProRationPeriods = calculateProRationBeforeFirstBillingPeriod(startDate, firstBillingCycleDate, billingPeriod);
             if (leadingProRationPeriods != null && leadingProRationPeriods.compareTo(BigDecimal.ZERO) > 0) {
                 // Not common - add info in the logs for debugging purposes
-                final RecurringInvoiceItemData itemData = new RecurringInvoiceItemData(startDate, billingIntervalDetail.getFirstBillingCycleDate(), leadingProRationPeriods);
+                final RecurringInvoiceItemData itemData = new RecurringInvoiceItemData(startDate, firstBillingCycleDate, leadingProRationPeriods);
                 log.info("Adding pro-ration: {}", itemData);
                 results.add(itemData);
             }
@@ -88,13 +89,18 @@ public class InAdvanceBillingMode implements BillingMode {
         // - If endDate != null and targetDate is after endDate => this is the endDate and will lead to a trailing pro-ration
         // - If not, this is the last billingCycleDate calculation right after the targetDate
         //
-        final LocalDate effectiveEndDate = billingIntervalDetail.getEffectiveEndDate();
+        final LocalDate effectiveEndDate;
+        if (endDate != null) {
+            effectiveEndDate = calculateEffectiveEndDate(firstBillingCycleDate, targetDate, endDate, billingPeriod);
+        } else {
+            effectiveEndDate = calculateEffectiveEndDate(firstBillingCycleDate, targetDate, billingPeriod);
+        }
 
         //
         // Based on what we calculated previously, code recompute one more time the numberOfWholeBillingPeriods
         //
-        final LocalDate lastBillingCycleDate = billingIntervalDetail.getLastBillingCycleDate();
-        final int numberOfWholeBillingPeriods = calculateNumberOfWholeBillingPeriods(billingIntervalDetail.getFirstBillingCycleDate(), lastBillingCycleDate, billingPeriod);
+        final LocalDate lastBillingCycleDate = calculateLastBillingCycleDateBefore(effectiveEndDate, firstBillingCycleDate, billingCycleDayLocal, billingPeriod);
+        final int numberOfWholeBillingPeriods = calculateNumberOfWholeBillingPeriods(firstBillingCycleDate, lastBillingCycleDate, billingPeriod);
         final int numberOfMonthsPerBillingPeriod = billingPeriod.getNumberOfMonths();
 
         for (int i = 0; i < numberOfWholeBillingPeriods; i++) {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/generator/TestInvoiceDateUtils.java b/invoice/src/test/java/com/ning/billing/invoice/generator/TestInvoiceDateUtils.java
index 5728847..2ee9dff 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/generator/TestInvoiceDateUtils.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/generator/TestInvoiceDateUtils.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.generator;
 
 import java.math.BigDecimal;
 
+import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -28,6 +29,22 @@ import com.ning.billing.invoice.InvoiceTestSuiteNoDB;
 public class TestInvoiceDateUtils extends InvoiceTestSuiteNoDB {
 
     @Test(groups = "fast")
+    public void testLastBCDShouldNotBeBeforePreviousBCD() throws Exception {
+        final LocalDate from = new LocalDate("2012-07-16");
+        final LocalDate previousBCD = new LocalDate("2012-08-15");
+        final int bcdLocal = 15;
+        final LocalDate lastBCD = InvoiceDateUtils.calculateLastBillingCycleDateBefore(from, previousBCD, bcdLocal, BillingPeriod.MONTHLY);
+        Assert.assertEquals(lastBCD, new LocalDate("2012-08-15"));
+    }
+
+    @Test(groups = "fast")
+    public void testNextBCDShouldNotBeInThePast() throws Exception {
+        final LocalDate from = new LocalDate("2012-07-16");
+        final LocalDate to = InvoiceDateUtils.calculateBillingCycleDateOnOrAfter(from, 15);
+        Assert.assertEquals(to, new LocalDate("2012-08-15"));
+    }
+
+    @Test(groups = "fast")
     public void testProRationAfterLastBillingCycleDate() throws Exception {
         final LocalDate endDate = new LocalDate("2012-06-02");
         final LocalDate previousBillThroughDate = new LocalDate("2012-03-02");
@@ -36,6 +53,67 @@ public class TestInvoiceDateUtils extends InvoiceTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
+    public void testBeforeBCDWithAfter() throws Exception {
+        final LocalDate from = new LocalDate("2012-03-02");
+        final LocalDate to = InvoiceDateUtils.calculateBillingCycleDateAfter(from, 3);
+        Assert.assertEquals(to, new LocalDate("2012-03-03"));
+    }
+
+    @Test(groups = "fast")
+    public void testEqualBCDWithAfter() throws Exception {
+        final LocalDate from = new LocalDate("2012-03-03");
+        final LocalDate to = InvoiceDateUtils.calculateBillingCycleDateAfter(from, 3);
+        Assert.assertEquals(to, new LocalDate("2012-04-03"));
+    }
+
+    @Test(groups = "fast")
+    public void testAfterBCDWithAfter() throws Exception {
+        final LocalDate from = new LocalDate("2012-03-04");
+        final LocalDate to = InvoiceDateUtils.calculateBillingCycleDateAfter(from, 3);
+        Assert.assertEquals(to, new LocalDate("2012-04-03"));
+    }
+
+    @Test(groups = "fast")
+    public void testBeforeBCDWithOnOrAfter() throws Exception {
+        final LocalDate from = new LocalDate("2012-03-02");
+        final LocalDate to = InvoiceDateUtils.calculateBillingCycleDateOnOrAfter(from, 3);
+        Assert.assertEquals(to, new LocalDate("2012-03-03"));
+    }
+
+    @Test(groups = "fast")
+    public void testEqualBCDWithOnOrAfter() throws Exception {
+        final LocalDate from = new LocalDate("2012-03-03");
+        final LocalDate to = InvoiceDateUtils.calculateBillingCycleDateOnOrAfter(from, 3);
+        Assert.assertEquals(to, new LocalDate("2012-03-03"));
+    }
+
+    @Test(groups = "fast")
+    public void testAfterBCDWithOnOrAfter() throws Exception {
+        final LocalDate from = new LocalDate("2012-03-04");
+        final LocalDate to = InvoiceDateUtils.calculateBillingCycleDateOnOrAfter(from, 3);
+        Assert.assertEquals(to, new LocalDate("2012-04-03"));
+    }
+
+    @Test(groups = "fast")
+    public void testEffectiveEndDate() throws Exception {
+        final LocalDate firstBCD = new LocalDate(2012, 7, 16);
+        final LocalDate targetDate = new LocalDate(2012, 8, 16);
+        final BillingPeriod billingPeriod = BillingPeriod.MONTHLY;
+        final LocalDate effectiveEndDate = InvoiceDateUtils.calculateEffectiveEndDate(firstBCD, targetDate, billingPeriod);
+        // TODO should that be 2012-09-15?
+        Assert.assertEquals(effectiveEndDate, new LocalDate(2012, 9, 16));
+    }
+
+    @Test(groups = "fast")
+    public void testLastBCD() throws Exception {
+        final LocalDate firstBCD = new LocalDate(2012, 7, 16);
+        final LocalDate effectiveEndDate = new LocalDate(2012, 9, 15);
+        final BillingPeriod billingPeriod = BillingPeriod.MONTHLY;
+        final LocalDate lastBCD = InvoiceDateUtils.calculateLastBillingCycleDateBefore(effectiveEndDate, firstBCD, 16, billingPeriod);
+        Assert.assertEquals(lastBCD, new LocalDate(2012, 8, 16));
+    }
+
+    @Test(groups = "fast")
     public void testCalculateNbOfBillingPeriods() throws Exception {
         final LocalDate firstBCD = new LocalDate(2012, 7, 16);
         final LocalDate lastBCD = new LocalDate(2012, 9, 16);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java b/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java
index f32427d..6037eeb 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java
@@ -36,11 +36,6 @@ public class MockBillingEventSet extends TreeSet<BillingEvent> implements Billin
     }
 
     @Override
-    public boolean isLast(final BillingEvent event) {
-        return event == last();
-    }
-
-    @Override
     public boolean isAccountAutoInvoiceOff() {
         return isAccountInvoiceOff;
     }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
index 03d7450..728bcce 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -194,7 +194,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
         final LocalDate endDate = new LocalDate("2012-11-26");
 
 
-        ((ClockMock) clock).setTime(new DateTime(2012, 10, 26, 1, 12, 23, DateTimeZone.UTC));
+        ((ClockMock) clock).setTime(new DateTime(2012, 10, 13, 1, 12, 23, DateTimeZone.UTC));
 
         final DateAndTimeZoneContext dateAndTimeZoneContext = new DateAndTimeZoneContext(clock.getUTCNow(), DateTimeZone.forID("Pacific/Pitcairn"), clock);
 
@@ -217,10 +217,6 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
         final LocalDate receivedTargetDate = new LocalDate(receivedDate, DateTimeZone.forID("Pacific/Pitcairn"));
         Assert.assertEquals(receivedTargetDate, endDate);
 
-        Assert.assertTrue(receivedDate.compareTo(new DateTime(2012, 11, 26, 9 /* 1 + 8 for Pitcairn */, 12, 23, DateTimeZone.UTC)) >= 0);
-        Assert.assertTrue(receivedDate.compareTo(new DateTime(2012, 11, 26, 9, 13, 0, DateTimeZone.UTC)) <= 0);
-
+        Assert.assertTrue(receivedDate.compareTo(new DateTime(2012, 11, 27, 1, 12, 23, DateTimeZone.UTC)) <= 0);
     }
-
-    //MDW add a test to cover when the account auto-invoice-off tag is present
 }

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

diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index c4b1329..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.3-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-jaxrs</artifactId>
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/RefundJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/RefundJson.java
index 73a83dd..1c17625 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/RefundJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/RefundJson.java
@@ -42,6 +42,7 @@ public class RefundJson extends JsonBase {
     private final Boolean isAdjusted;
     private final DateTime requestedDate;
     private final DateTime effectiveDate;
+    private final String status;
     private final List<InvoiceItemJson> adjustments;
 
     @JsonCreator
@@ -49,6 +50,7 @@ public class RefundJson extends JsonBase {
                       @JsonProperty("paymentId") final String paymentId,
                       @JsonProperty("amount") final BigDecimal amount,
                       @JsonProperty("currency") final String currency,
+                      @JsonProperty("status") final String status,
                       @JsonProperty("adjusted") final Boolean isAdjusted,
                       @JsonProperty("requestedDate") final DateTime requestedDate,
                       @JsonProperty("effectiveDate") final DateTime effectiveDate,
@@ -59,6 +61,7 @@ public class RefundJson extends JsonBase {
         this.paymentId = paymentId;
         this.amount = amount;
         this.currency = currency;
+        this.status = status;
         this.isAdjusted = isAdjusted;
         this.requestedDate = requestedDate;
         this.effectiveDate = effectiveDate;
@@ -71,7 +74,7 @@ public class RefundJson extends JsonBase {
 
     public RefundJson(final Refund refund, @Nullable final List<InvoiceItem> adjustments, @Nullable final List<AuditLog> auditLogs) {
         this(refund.getId().toString(), refund.getPaymentId().toString(), refund.getRefundAmount(), refund.getCurrency().toString(),
-             refund.isAdjusted(), refund.getEffectiveDate(), refund.getEffectiveDate(),
+             refund.getRefundStatus().toString(), refund.isAdjusted(), refund.getEffectiveDate(), refund.getEffectiveDate(),
              adjustments == null ? null : ImmutableList.<InvoiceItemJson>copyOf(Collections2.transform(adjustments, new Function<InvoiceItem, InvoiceItemJson>() {
                  @Override
                  public InvoiceItemJson apply(@Nullable final InvoiceItem input) {
@@ -113,6 +116,8 @@ public class RefundJson extends JsonBase {
         return adjustments;
     }
 
+    public String getStatus() { return status; }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
@@ -121,6 +126,7 @@ public class RefundJson extends JsonBase {
         sb.append(", paymentId='").append(paymentId).append('\'');
         sb.append(", amount=").append(amount);
         sb.append(", currency=").append(currency);
+        sb.append(", status=").append(status);
         sb.append(", isAdjusted=").append(isAdjusted);
         sb.append(", requestedDate=").append(requestedDate);
         sb.append(", effectiveDate=").append(effectiveDate);
@@ -135,6 +141,7 @@ public class RefundJson extends JsonBase {
         result = 31 * result + (paymentId != null ? paymentId.hashCode() : 0);
         result = 31 * result + (amount != null ? amount.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (status != null ? status.hashCode() : 0);
         result = 31 * result + (isAdjusted != null ? isAdjusted.hashCode() : 0);
         result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
         result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
@@ -222,6 +229,14 @@ public class RefundJson extends JsonBase {
             return false;
         }
 
+        if (status == null) {
+            if (other.status != null) {
+                return false;
+            }
+        } else if (!status.equals(other.status)) {
+            return false;
+        }
+
         if (adjustments == null) {
             if (other.adjustments != null) {
                 return false;
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/AccountApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/AccountApiExceptionMapper.java
index 26a1908..a9484fb 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/AccountApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/AccountApiExceptionMapper.java
@@ -54,8 +54,10 @@ public class AccountApiExceptionMapper extends ExceptionMapperBase implements Ex
             return buildBadRequestResponse(exception, uriInfo);
         } else if (exception.getCode() == ErrorCode.ACCOUNT_UPDATE_FAILED.getCode()) {
             return buildInternalErrorResponse(exception, uriInfo);
+        } else if (exception.getCode() == ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_RECORD_ID.getCode()) {
+            return buildNotFoundResponse(exception, uriInfo);
         } else {
-            return buildBadRequestResponse(exception, uriInfo);
+            return fallback(exception, uriInfo);
         }
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/BlockingApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/BlockingApiExceptionMapper.java
index 7f23095..aed9c10 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/BlockingApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/BlockingApiExceptionMapper.java
@@ -37,6 +37,6 @@ public class BlockingApiExceptionMapper extends ExceptionMapperBase implements E
 
     @Override
     public Response toResponse(final BlockingApiException exception) {
-        return buildBadRequestResponse(exception, uriInfo);
+        return fallback(exception, uriInfo);
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/CatalogApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/CatalogApiExceptionMapper.java
index d5a464d..fe85c6f 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/CatalogApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/CatalogApiExceptionMapper.java
@@ -37,6 +37,6 @@ public class CatalogApiExceptionMapper extends ExceptionMapperBase implements Ex
 
     @Override
     public Response toResponse(final CatalogApiException exception) {
-        return buildBadRequestResponse(exception, uriInfo);
+        return fallback(exception, uriInfo);
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EmailApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EmailApiExceptionMapper.java
index e619556..ffdf1c9 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EmailApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EmailApiExceptionMapper.java
@@ -37,6 +37,6 @@ public class EmailApiExceptionMapper extends ExceptionMapperBase implements Exce
 
     @Override
     public Response toResponse(final EmailApiException exception) {
-        return buildInternalErrorResponse(exception, uriInfo);
+        return fallback(exception, uriInfo);
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntitlementApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntitlementApiExceptionMapper.java
index 5dcfa3b..c4506cb 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntitlementApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntitlementApiExceptionMapper.java
@@ -45,7 +45,7 @@ public class EntitlementApiExceptionMapper extends ExceptionMapperBase implement
         } else if (exception.getCode() == ErrorCode.SUB_INVALID_SUBSCRIPTION_ID.getCode()) {
             return buildNotFoundResponse(exception, uriInfo);
         } else {
-            return buildBadRequestResponse(exception, uriInfo);
+            return fallback(exception, uriInfo);
         }
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntityPersistenceExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntityPersistenceExceptionMapper.java
index d7c6194..f9301b7 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntityPersistenceExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntityPersistenceExceptionMapper.java
@@ -37,6 +37,6 @@ public class EntityPersistenceExceptionMapper extends ExceptionMapperBase implem
 
     @Override
     public Response toResponse(final EntityPersistenceException exception) {
-        return buildInternalErrorResponse(exception, uriInfo);
+        return fallback(exception, uriInfo);
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/ExceptionMapperBase.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/ExceptionMapperBase.java
index 2c82fa6..c070c79 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/ExceptionMapperBase.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/ExceptionMapperBase.java
@@ -24,7 +24,22 @@ import javax.ws.rs.core.UriInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.entitlement.api.BlockingApiException;
+import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.entitlement.api.SubscriptionApiException;
+import com.ning.billing.entity.EntityPersistenceException;
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.jaxrs.json.BillingExceptionJson;
+import com.ning.billing.overdue.OverdueApiException;
+import com.ning.billing.payment.api.PaymentApiException;
+import com.ning.billing.subscription.api.SubscriptionBillingApiException;
+import com.ning.billing.subscription.api.timeline.SubscriptionBaseRepairException;
+import com.ning.billing.util.api.TagApiException;
+import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.email.EmailApiException;
 import com.ning.billing.util.jackson.ObjectMapper;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
@@ -34,6 +49,67 @@ public abstract class ExceptionMapperBase {
     private static final Logger log = LoggerFactory.getLogger(ExceptionMapperBase.class);
     private static final ObjectMapper mapper = new ObjectMapper();
 
+    protected Response fallback(final Exception exception, final UriInfo uriInfo) {
+        if (exception.getCause() == null) {
+            return buildBadRequestResponse(exception, uriInfo);
+        } else {
+            return doFallback(exception, uriInfo);
+        }
+    }
+
+    private Response doFallback(final Exception exception, final UriInfo uriInfo) {
+        if (exception.getCause() == null || !(exception.getCause() instanceof BillingExceptionBase)) {
+            return buildBadRequestResponse(exception, uriInfo);
+        }
+
+        final BillingExceptionBase cause = (BillingExceptionBase) exception.getCause();
+        if (cause instanceof AccountApiException) {
+            final AccountApiExceptionMapper mapper = new AccountApiExceptionMapper(uriInfo);
+            return mapper.toResponse((AccountApiException) cause);
+        } else if (cause instanceof BlockingApiException) {
+            final BlockingApiExceptionMapper mapper = new BlockingApiExceptionMapper(uriInfo);
+            return mapper.toResponse((BlockingApiException) cause);
+        } else if (cause instanceof CatalogApiException) {
+            final CatalogApiExceptionMapper mapper = new CatalogApiExceptionMapper(uriInfo);
+            return mapper.toResponse((CatalogApiException) cause);
+        } else if (cause instanceof EmailApiException) {
+            final EmailApiExceptionMapper mapper = new EmailApiExceptionMapper(uriInfo);
+            return mapper.toResponse((EmailApiException) cause);
+        } else if (cause instanceof EntitlementApiException) {
+            final EntitlementApiExceptionMapper mapper = new EntitlementApiExceptionMapper(uriInfo);
+            return mapper.toResponse((EntitlementApiException) cause);
+        } else if (cause instanceof EntityPersistenceException) {
+            final EntityPersistenceExceptionMapper mapper = new EntityPersistenceExceptionMapper(uriInfo);
+            return mapper.toResponse((EntityPersistenceException) cause);
+        } else if (cause instanceof InvoiceApiException) {
+            final InvoiceApiExceptionMapper mapper = new InvoiceApiExceptionMapper(uriInfo);
+            return mapper.toResponse((InvoiceApiException) cause);
+        } else if (cause instanceof OverdueApiException) {
+            final OverdueApiExceptionMapper mapper = new OverdueApiExceptionMapper(uriInfo);
+            return mapper.toResponse((OverdueApiException) cause);
+        } else if (cause instanceof PaymentApiException) {
+            final PaymentApiExceptionMapper mapper = new PaymentApiExceptionMapper(uriInfo);
+            return mapper.toResponse((PaymentApiException) cause);
+        } else if (cause instanceof SubscriptionApiException) {
+            final SubscriptionApiExceptionMapper mapper = new SubscriptionApiExceptionMapper(uriInfo);
+            return mapper.toResponse((SubscriptionApiException) cause);
+        } else if (cause instanceof SubscriptionBillingApiException) {
+            final SubscriptionBillingApiExceptionMapper mapper = new SubscriptionBillingApiExceptionMapper(uriInfo);
+            return mapper.toResponse((SubscriptionBillingApiException) cause);
+        } else if (cause instanceof SubscriptionBaseRepairException) {
+            final SubscriptionRepairExceptionMapper mapper = new SubscriptionRepairExceptionMapper(uriInfo);
+            return mapper.toResponse((SubscriptionBaseRepairException) cause);
+        } else if (cause instanceof TagApiException) {
+            final TagApiExceptionMapper mapper = new TagApiExceptionMapper(uriInfo);
+            return mapper.toResponse((TagApiException) cause);
+        } else if (cause instanceof TagDefinitionApiException) {
+            final TagDefinitionApiExceptionMapper mapper = new TagDefinitionApiExceptionMapper(uriInfo);
+            return mapper.toResponse((TagDefinitionApiException) cause);
+        } else {
+            return buildBadRequestResponse(cause, uriInfo);
+        }
+    }
+
     protected Response buildConflictingRequestResponse(final Exception e, final UriInfo uriInfo) {
         // Log the full stacktrace
         log.warn("Conflicting request", e);
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/InvoiceApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/InvoiceApiExceptionMapper.java
index 6dc12e4..8f19f44 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/InvoiceApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/InvoiceApiExceptionMapper.java
@@ -69,7 +69,7 @@ public class InvoiceApiExceptionMapper extends ExceptionMapperBase implements Ex
         } else if (exception.getCode() == ErrorCode.EXTERNAL_CHARGE_AMOUNT_INVALID.getCode()) {
             return buildBadRequestResponse(exception, uriInfo);
         } else {
-            return buildBadRequestResponse(exception, uriInfo);
+            return fallback(exception, uriInfo);
         }
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueApiExceptionMapper.java
index 13b5e44..061f0f0 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueApiExceptionMapper.java
@@ -37,6 +37,6 @@ public class OverdueApiExceptionMapper extends ExceptionMapperBase implements Ex
 
     @Override
     public Response toResponse(final OverdueApiException exception) {
-        return buildBadRequestResponse(exception, uriInfo);
+        return fallback(exception, uriInfo);
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/PaymentApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
index f4678e2..e1bafc0 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
@@ -99,7 +99,7 @@ public class PaymentApiExceptionMapper extends ExceptionMapperBase implements Ex
         } else if (exception.getCode() == ErrorCode.PAYMENT_UPD_PAYMENT_PROVIDER_ACCOUNT.getCode()) {
             return buildInternalErrorResponse(exception, uriInfo);
         } else {
-            return buildBadRequestResponse(exception, uriInfo);
+            return fallback(exception, uriInfo);
         }
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionApiExceptionMapper.java
index cdcc153..36a9bd0 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionApiExceptionMapper.java
@@ -25,7 +25,6 @@ import javax.ws.rs.ext.Provider;
 
 import com.ning.billing.ErrorCode;
 import com.ning.billing.entitlement.api.SubscriptionApiException;
-import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
 
 @Singleton
 @Provider
@@ -81,10 +80,10 @@ public class SubscriptionApiExceptionMapper extends ExceptionMapperBase implemen
             return buildNotFoundResponse(exception, uriInfo);
         } else if (exception.getCode() == ErrorCode.SUB_RECREATE_BAD_STATE.getCode()) {
             return buildInternalErrorResponse(exception, uriInfo);
-        }  else if (exception.getCode() == ErrorCode.SUB_UNCANCEL_BAD_STATE.getCode()) {
+        } else if (exception.getCode() == ErrorCode.SUB_UNCANCEL_BAD_STATE.getCode()) {
             return buildInternalErrorResponse(exception, uriInfo);
         } else {
-            return buildBadRequestResponse(exception, uriInfo);
+            return fallback(exception, uriInfo);
         }
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionBillingApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionBillingApiExceptionMapper.java
index 2b38fe8..b48434e 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionBillingApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionBillingApiExceptionMapper.java
@@ -37,6 +37,6 @@ public class SubscriptionBillingApiExceptionMapper extends ExceptionMapperBase i
 
     @Override
     public Response toResponse(final SubscriptionBillingApiException exception) {
-        return buildBadRequestResponse(exception, uriInfo);
+        return fallback(exception, uriInfo);
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionRepairExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionRepairExceptionMapper.java
index 0947a82..f00f0c0 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionRepairExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionRepairExceptionMapper.java
@@ -69,7 +69,7 @@ public class SubscriptionRepairExceptionMapper extends ExceptionMapperBase imple
         } else if (exception.getCode() == ErrorCode.SUB_REPAIR_VIEW_CHANGED.getCode()) {
             return buildBadRequestResponse(exception, uriInfo);
         } else {
-            return buildBadRequestResponse(exception, uriInfo);
+            return fallback(exception, uriInfo);
         }
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
index 5ca3936..f464200 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
@@ -61,6 +61,7 @@ import com.ning.billing.events.InvoiceCreationInternalEvent;
 import com.ning.billing.events.NullInvoiceInternalEvent;
 import com.ning.billing.events.PaymentErrorInternalEvent;
 import com.ning.billing.events.PaymentInfoInternalEvent;
+import com.ning.billing.events.PaymentPluginErrorInternalEvent;
 import com.ning.billing.jaxrs.json.CustomFieldJson;
 import com.ning.billing.jaxrs.json.SubscriptionJson;
 import com.ning.billing.jaxrs.util.Context;
@@ -341,6 +342,12 @@ public class SubscriptionResource extends JaxRsResourceBase {
             log.info(String.format("Got event PaymentError token = %s ", event.getUserToken()));
             notifyForCompletion();
         }
+
+        @Override
+        public void onPaymentPluginError(final PaymentPluginErrorInternalEvent event) {
+            log.info(String.format("Got event PaymentPluginError token = %s ", event.getUserToken()));
+            notifyForCompletion();
+        }
     }
 
     private interface EntitlementCallCompletionCallback<T> {
diff --git a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestRefundJson.java b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestRefundJson.java
index 544c23d..6e4e71a 100644
--- a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestRefundJson.java
+++ b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestRefundJson.java
@@ -27,6 +27,7 @@ import org.testng.annotations.Test;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.jaxrs.JaxrsTestSuiteNoDB;
+import com.ning.billing.payment.api.RefundStatus;
 
 import com.google.common.collect.ImmutableList;
 
@@ -43,9 +44,10 @@ public class TestRefundJson extends JaxrsTestSuiteNoDB {
         final boolean isAdjusted = true;
         final DateTime requestedDate = clock.getUTCNow();
         final DateTime effectiveDate = clock.getUTCNow();
+        final RefundStatus status = RefundStatus.COMPLETED;
         final List<InvoiceItemJson> adjustments = ImmutableList.<InvoiceItemJson>of(createInvoiceItemJson());
         final List<AuditLogJson> auditLogs = createAuditLogsJson(clock.getUTCNow());
-        final RefundJson refundJson = new RefundJson(refundId, paymentId, amount, currency, isAdjusted, requestedDate,
+        final RefundJson refundJson = new RefundJson(refundId, paymentId, amount, currency, status.toString(), isAdjusted, requestedDate,
                                                      effectiveDate, adjustments, auditLogs);
         Assert.assertEquals(refundJson.getRefundId(), refundId);
         Assert.assertEquals(refundJson.getPaymentId(), paymentId);

junction/pom.xml 12(+11 -1)

diff --git a/junction/pom.xml b/junction/pom.xml
index 71d8d07..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.3-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-junction</artifactId>
@@ -50,6 +50,11 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>com.jayway.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-api</artifactId>
         </dependency>
@@ -81,6 +86,11 @@
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-subscription</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
             <artifactId>killbill-util</artifactId>
         </dependency>
         <dependency>
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java
index e2cca24..bc98975 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java
@@ -25,22 +25,23 @@ import java.util.TreeSet;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicLong;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 
 import com.ning.billing.account.api.Account;
+import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
-import com.ning.billing.entitlement.api.BlockingStateType;
-import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
-import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.entitlement.api.BlockingState;
-import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.junction.BillingEvent;
 import com.ning.billing.junction.BillingModeType;
 import com.ning.billing.junction.BlockingInternalApi;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.inject.Inject;
@@ -52,8 +53,9 @@ public class BlockingCalculator {
     private final BlockingInternalApi blockingApi;
 
     protected static class DisabledDuration {
+
         private final DateTime start;
-        private final DateTime end;
+        private DateTime end;
 
         public DisabledDuration(final DateTime start, final DateTime end) {
             this.start = start;
@@ -68,6 +70,9 @@ public class BlockingCalculator {
             return end;
         }
 
+        public void setEnd(final DateTime end) {
+            this.end = end;
+        }
     }
 
     @Inject
@@ -92,11 +97,9 @@ public class BlockingCalculator {
         final SortedSet<BillingEvent> billingEventsToAdd = new TreeSet<BillingEvent>();
         final SortedSet<BillingEvent> billingEventsToRemove = new TreeSet<BillingEvent>();
 
+        final List<BlockingState> blockingEvents = blockingApi.getBlockingAllForAccount(context);
+        final List<DisabledDuration> blockingDurations = createBlockingDurations(blockingEvents);
         for (final UUID bundleId : bundleMap.keySet()) {
-            final List<BlockingState> blockingEvents = blockingApi.getBlockingAll(bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE, context);
-            blockingEvents.addAll(blockingApi.getBlockingAll(account.getId(), BlockingStateType.ACCOUNT, context));
-            final List<DisabledDuration> blockingDurations = createBlockingDurations(blockingEvents);
-
             for (final SubscriptionBase subscription : bundleMap.get(bundleId)) {
                 billingEventsToAdd.addAll(createNewEvents(blockingDurations, billingEvents, account, subscription));
                 billingEventsToRemove.addAll(eventsToRemove(blockingDurations, billingEvents, subscription));
@@ -252,31 +255,56 @@ public class BlockingCalculator {
         return result;
     }
 
-
     // In ascending order
-    protected List<DisabledDuration> createBlockingDurations(final List<BlockingState> overdueBundleEvents) {
+    protected List<DisabledDuration> createBlockingDurations(final Iterable<BlockingState> overdueBundleEvents) {
         final List<DisabledDuration> result = new ArrayList<BlockingCalculator.DisabledDuration>();
         // Earliest blocking event
         BlockingState first = null;
 
+        int blockedNesting = 0;
+        BlockingState lastOne = null;
         for (final BlockingState e : overdueBundleEvents) {
-            if (e.isBlockBilling() && first == null) {
+            lastOne = e;
+            if (e.isBlockBilling() && blockedNesting == 0) {
                 // First blocking event of contiguous series of blocking events
                 first = e;
-            } else if (first != null && !e.isBlockBilling()) {
-                // End of the interval
-                result.add(new DisabledDuration(first.getEffectiveDate(), e.getEffectiveDate()));
-                first = null;
+                blockedNesting++;
+            } else if (e.isBlockBilling() && blockedNesting > 0) {
+                // Nest blocking states
+                blockedNesting++;
+            } else if (!e.isBlockBilling() && blockedNesting > 0) {
+                blockedNesting--;
+                if (blockedNesting == 0) {
+                    // End of the interval
+                    addDisabledDuration(result, first, e);
+                    first = null;
+                }
             }
         }
 
         if (first != null) { // found a transition to disabled with no terminating event
-            result.add(new DisabledDuration(first.getEffectiveDate(), null));
+            addDisabledDuration(result, first, lastOne.isBlockBilling() ? null : lastOne);
         }
 
         return result;
     }
 
+    private void addDisabledDuration(final List<DisabledDuration> result, final BlockingState firstBlocking, @Nullable final BlockingState firstNonBlocking) {
+        final DisabledDuration lastOne;
+        if (!result.isEmpty()) {
+            lastOne = result.get(result.size() - 1);
+        } else {
+            lastOne = null;
+        }
+
+        final DateTime endDate = firstNonBlocking == null ? null : firstNonBlocking.getEffectiveDate();
+        if (lastOne != null && lastOne.getEnd().compareTo(firstBlocking.getEffectiveDate()) == 0) {
+            lastOne.setEnd(endDate);
+        } else {
+            result.add(new DisabledDuration(firstBlocking.getEffectiveDate(), endDate));
+        }
+    }
+
     @VisibleForTesting
     static AtomicLong getGlobalTotalOrder() {
         return globaltotalOrder;
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java
index 03df1e7..c55aaa3 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java
@@ -55,10 +55,6 @@ public class DefaultBillingEventSet extends TreeSet<BillingEvent> implements Sor
         this.subscriptionIdsWithAutoInvoiceOff = subscriptionIdsWithAutoInvoiceOff;
     }
 
-    public boolean isLast(final BillingEvent event) {
-        return last() == event;
-    }
-
     @Override
     public String toString() {
         return "DefaultBillingEventSet [accountAutoInvoiceOff=" + accountAutoInvoiceOff
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
index 83ef135..defc439 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
@@ -28,18 +28,18 @@ import org.slf4j.LoggerFactory;
 import com.ning.billing.ObjectType;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountInternalApi;
 import com.ning.billing.account.api.MutableAccountData;
+import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.CatalogService;
-import com.ning.billing.subscription.api.SubscriptionBase;
-import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
-import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.events.EffectiveSubscriptionInternalEvent;
-import com.ning.billing.account.api.AccountInternalApi;
-import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 import com.ning.billing.junction.BillingEvent;
 import com.ning.billing.junction.BillingEventSet;
 import com.ning.billing.junction.BillingInternalApi;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
 import com.ning.billing.tag.TagInternalApi;
 import com.ning.billing.util.tag.ControlTagType;
 import com.ning.billing.util.tag.Tag;
diff --git a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
index 0d4307d..b74b247 100644
--- a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
+++ b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
@@ -18,19 +18,16 @@ package com.ning.billing.junction.glue;
 
 import org.skife.config.ConfigSource;
 
-import com.ning.billing.catalog.MockCatalogModule;
 import com.ning.billing.entitlement.api.svcs.DefaultInternalBlockingApi;
 import com.ning.billing.entitlement.block.BlockingChecker;
 import com.ning.billing.entitlement.block.MockBlockingChecker;
 import com.ning.billing.entitlement.dao.BlockingStateDao;
 import com.ning.billing.entitlement.dao.MockBlockingStateDao;
-import com.ning.billing.mock.glue.MockAccountModule;
+import com.ning.billing.junction.BlockingInternalApi;
 import com.ning.billing.mock.glue.MockEntitlementModule;
-import com.ning.billing.mock.glue.MockSubscriptionModule;
 import com.ning.billing.util.glue.CacheModule;
 import com.ning.billing.util.glue.CallContextModule;
 import com.ning.billing.util.glue.MetricsModule;
-import com.ning.billing.junction.BlockingInternalApi;
 
 public class TestJunctionModule extends DefaultJunctionModule {
 
@@ -45,10 +42,6 @@ public class TestJunctionModule extends DefaultJunctionModule {
         install(new MetricsModule());
         install(new CacheModule(configSource));
         install(new CallContextModule());
-        install(new MockAccountModule());
-        install(new MockCatalogModule());
-        install(new MockSubscriptionModule());
-        install(new MockEntitlementModuleForJunction());
     }
 
     public class MockEntitlementModuleForJunction extends MockEntitlementModule {
@@ -67,6 +60,5 @@ public class TestJunctionModule extends DefaultJunctionModule {
         public void installBlockingChecker() {
             bind(BlockingChecker.class).to(MockBlockingChecker.class).asEagerSingleton();
         }
-
     }
 }
diff --git a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java
index faf6939..081d6aa 100644
--- a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java
+++ b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java
@@ -20,7 +20,10 @@ import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
 
 import com.ning.billing.GuicyKillbillTestNoDBModule;
+import com.ning.billing.catalog.MockCatalogModule;
+import com.ning.billing.mock.glue.MockAccountModule;
 import com.ning.billing.mock.glue.MockNonEntityDaoModule;
+import com.ning.billing.mock.glue.MockSubscriptionModule;
 import com.ning.billing.mock.glue.MockTagModule;
 import com.ning.billing.notificationq.MockNotificationQueueService;
 import com.ning.billing.notificationq.api.NotificationQueueConfig;
@@ -42,6 +45,10 @@ public class TestJunctionModuleNoDB extends TestJunctionModule {
         install(new GuicyKillbillTestNoDBModule());
         install(new MockNonEntityDaoModule());
         install(new InMemoryBusModule(configSource));
+        install(new MockAccountModule());
+        install(new MockCatalogModule());
+        install(new MockSubscriptionModule());
+        install(new MockEntitlementModuleForJunction());
         install(new MockTagModule());
         installNotificationQueue();
     }
diff --git a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
index ccac19f..32dff2d 100644
--- a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
+++ b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
@@ -19,9 +19,17 @@ package com.ning.billing.junction.glue;
 import org.skife.config.ConfigSource;
 
 import com.ning.billing.GuicyKillbillTestWithEmbeddedDBModule;
+import com.ning.billing.account.glue.DefaultAccountModule;
+import com.ning.billing.api.TestApiListener;
+import com.ning.billing.api.TestListenerStatus;
+import com.ning.billing.catalog.glue.CatalogModule;
+import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
+import com.ning.billing.junction.JunctionTestListenerStatus;
+import com.ning.billing.subscription.glue.DefaultSubscriptionModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.MetricsModule;
 import com.ning.billing.util.glue.NonEntityDaoModule;
+import com.ning.billing.util.glue.NotificationQueueModule;
 import com.ning.billing.util.glue.TagStoreModule;
 
 public class TestJunctionModuleWithEmbeddedDB extends TestJunctionModule {
@@ -36,8 +44,16 @@ public class TestJunctionModuleWithEmbeddedDB extends TestJunctionModule {
 
         install(new GuicyKillbillTestWithEmbeddedDBModule());
         install(new NonEntityDaoModule());
+        install(new CatalogModule(configSource));
+        install(new DefaultAccountModule(configSource));
+        install(new DefaultEntitlementModule(configSource));
+        install(new NotificationQueueModule(configSource));
+        install(new DefaultSubscriptionModule(configSource));
         install(new BusModule(configSource));
         install(new MetricsModule());
         install(new TagStoreModule());
+
+        bind(TestListenerStatus.class).to(JunctionTestListenerStatus.class).asEagerSingleton();
+        bind(TestApiListener.class).asEagerSingleton();
     }
 }
diff --git a/junction/src/test/java/com/ning/billing/junction/JunctionTestListenerStatus.java b/junction/src/test/java/com/ning/billing/junction/JunctionTestListenerStatus.java
new file mode 100644
index 0000000..a54976d
--- /dev/null
+++ b/junction/src/test/java/com/ning/billing/junction/JunctionTestListenerStatus.java
@@ -0,0 +1,57 @@
+/*
+ * 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.junction;
+
+import javax.inject.Inject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+
+import com.ning.billing.api.TestListenerStatus;
+
+public class JunctionTestListenerStatus implements TestListenerStatus {
+
+    private final Logger log = LoggerFactory.getLogger(JunctionTestListenerStatus.class);
+
+    private boolean isListenerFailed;
+    private String listenerFailedMsg;
+
+    @Inject
+    public JunctionTestListenerStatus() {
+        isListenerFailed = false;
+    }
+
+    @Override
+    public void failed(final String msg) {
+        this.isListenerFailed = true;
+        this.listenerFailedMsg = msg;
+    }
+
+    @Override
+    public void resetTestListenerStatus() {
+        this.isListenerFailed = false;
+        this.listenerFailedMsg = null;
+    }
+
+    public void assertListenerStatus() {
+        if (isListenerFailed) {
+            log.error(listenerFailedMsg);
+            Assert.fail(listenerFailedMsg);
+        }
+    }
+}
\ No newline at end of file
diff --git a/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java b/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
index 1c989e2..20750d0 100644
--- a/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
+++ b/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
@@ -16,34 +16,63 @@
 
 package com.ning.billing.junction;
 
+import java.net.URL;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 
 import com.ning.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.api.TestApiListener;
+import com.ning.billing.api.TestListenerStatus;
 import com.ning.billing.bus.api.PersistentBus;
+import com.ning.billing.catalog.DefaultCatalogService;
+import com.ning.billing.catalog.api.Catalog;
 import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.clock.ClockMock;
+import com.ning.billing.entitlement.DefaultEntitlementService;
+import com.ning.billing.entitlement.EntitlementService;
+import com.ning.billing.entitlement.api.EntitlementApi;
 import com.ning.billing.junction.glue.TestJunctionModuleWithEmbeddedDB;
-import com.ning.billing.junction.plumbing.billing.BlockingCalculator;
-import com.ning.billing.account.api.AccountInternalApi;
+import com.ning.billing.mock.MockAccountBuilder;
 import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
-import com.ning.billing.tag.TagInternalApi;
-import com.ning.billing.util.tag.dao.TagDao;
+import com.ning.billing.subscription.api.SubscriptionBaseService;
+import com.ning.billing.subscription.engine.core.DefaultSubscriptionBaseService;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.svcsapi.bus.BusService;
 
 import com.google.inject.Guice;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
+import com.google.inject.Stage;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
 
 public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWithEmbeddedDB {
 
+    protected static final Logger log = LoggerFactory.getLogger(JunctionTestSuiteWithEmbeddedDB.class);
+
+    // Be generous...
+    protected static final Long DELAY = 20000L;
+
     @Inject
-    protected AccountInternalApi accountInternalApi;
+    protected AccountUserApi accountApi;
     @Inject
-    protected BillingInternalApi billingInternalApi;
+    protected BlockingInternalApi blockingInternalApi;
     @Inject
-    protected BlockingCalculator blockingCalculator;
+    protected EntitlementApi entitlementApi;
     @Inject
-    protected BlockingInternalApi blockingInternalApi;
+    protected BillingInternalApi billingInternalApi;
     @Inject
     protected CatalogService catalogService;
     @Inject
@@ -51,24 +80,145 @@ public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestS
     @Inject
     protected PersistentBus bus;
     @Inject
-    protected TagDao tagDao;
+    protected TestApiListener testListener;
+    @Inject
+    protected TestListenerStatus testListenerStatus;
+    @Inject
+    protected BusService busService;
     @Inject
-    protected TagInternalApi tagInternalApi;
+    protected SubscriptionBaseService subscriptionBaseService;
+    @Inject
+    protected EntitlementService entitlementService;
+    @Inject
+    protected InternalCallContextFactory internalCallContextFactory;
+
+    protected Catalog catalog;
+
+    private void loadSystemPropertiesFromClasspath(final String resource) {
+        final URL url = JunctionTestSuiteWithEmbeddedDB.class.getResource(resource);
+        Assert.assertNotNull(url);
+
+        configSource.merge(url);
+    }
 
     @BeforeClass(groups = "slow")
     protected void beforeClass() throws Exception {
-        final Injector injector = Guice.createInjector(new TestJunctionModuleWithEmbeddedDB(configSource));
+        loadSystemPropertiesFromClasspath("/junction.properties");
+        final Injector injector = Guice.createInjector(Stage.PRODUCTION, new TestJunctionModuleWithEmbeddedDB(configSource));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "slow")
     public void beforeMethod() throws Exception {
         super.beforeMethod();
-        bus.start();
+        startTestFamework();
+        this.catalog = initCatalog(catalogService);
+
+        // Make sure we start with a clean state
+        assertListenerStatus();
     }
 
     @AfterMethod(groups = "slow")
-    public void afterMethod() {
-        bus.stop();
+    public void afterMethod() throws Exception {
+        // Make sure we finish in a clean state
+        assertListenerStatus();
+
+        stopTestFramework();
+    }
+
+    private Catalog initCatalog(final CatalogService catalogService) throws Exception {
+        ((DefaultCatalogService) catalogService).loadCatalog();
+        final Catalog catalog = catalogService.getFullCatalog();
+        assertNotNull(catalog);
+        return catalog;
+    }
+
+    private void startTestFamework() throws Exception {
+        log.debug("STARTING TEST FRAMEWORK");
+
+        resetTestListener(testListener, testListenerStatus);
+
+        resetClockToStartOfTest(clock);
+
+        startBusAndRegisterListener(busService, testListener);
+
+        restartSubscriptionService(subscriptionBaseService);
+        restartEntitlementService(entitlementService);
+
+        log.debug("STARTED TEST FRAMEWORK");
+    }
+
+    private void stopTestFramework() throws Exception {
+        log.debug("STOPPING TEST FRAMEWORK");
+        stopBusAndUnregisterListener(busService, testListener);
+        stopSubscriptionService(subscriptionBaseService);
+        stopEntitlementService(entitlementService);
+        log.debug("STOPPED TEST FRAMEWORK");
+    }
+
+    private void resetTestListener(final TestApiListener testListener, final TestListenerStatus testListenerStatus) {
+        // RESET LIST OF EXPECTED EVENTS
+        if (testListener != null) {
+            testListener.reset();
+            testListenerStatus.resetTestListenerStatus();
+        }
+    }
+
+    private void resetClockToStartOfTest(final ClockMock clock) {
+        clock.resetDeltaFromReality();
+
+        // Date at which all tests start-- we create the date object here after the system properties which set the JVM in UTC have been set.
+        final DateTime testStartDate = new DateTime(2012, 5, 7, 0, 3, 42, 0);
+        clock.setDeltaFromReality(testStartDate.getMillis() - clock.getUTCNow().getMillis());
+    }
+
+    private void startBusAndRegisterListener(final BusService busService, final TestApiListener testListener) throws Exception {
+        busService.getBus().start();
+        busService.getBus().register(testListener);
+    }
+
+    private void restartSubscriptionService(final SubscriptionBaseService subscriptionBaseService) {
+        // START NOTIFICATION QUEUE FOR SUBSCRIPTION
+        ((DefaultSubscriptionBaseService) subscriptionBaseService).initialize();
+        ((DefaultSubscriptionBaseService) subscriptionBaseService).start();
+    }
+
+    private void restartEntitlementService(final EntitlementService entitlementService) {
+        // START NOTIFICATION QUEUE FOR ENTITLEMENT
+        ((DefaultEntitlementService) entitlementService).initialize();
+        ((DefaultEntitlementService) entitlementService).start();
+    }
+
+    private void stopBusAndUnregisterListener(final BusService busService, final TestApiListener testListener) throws Exception {
+        busService.getBus().unregister(testListener);
+        busService.getBus().stop();
+    }
+
+    private void stopSubscriptionService(final SubscriptionBaseService subscriptionBaseService) throws Exception {
+        ((DefaultSubscriptionBaseService) subscriptionBaseService).stop();
+    }
+
+    private void stopEntitlementService(final EntitlementService entitlementService) throws Exception {
+        ((DefaultEntitlementService) entitlementService).stop();
+    }
+
+    protected AccountData getAccountData(final int billingDay) {
+        return new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
+                                       .firstNameLength(6)
+                                       .email(UUID.randomUUID().toString().substring(1, 8))
+                                       .phone(UUID.randomUUID().toString().substring(1, 8))
+                                       .migrated(false)
+                                       .isNotifiedForInvoices(false)
+                                       .externalKey(UUID.randomUUID().toString().substring(1, 8))
+                                       .billingCycleDayLocal(billingDay)
+                                       .currency(Currency.USD)
+                                       .paymentMethodId(UUID.randomUUID())
+                                       .timeZone(DateTimeZone.UTC)
+                                       .build();
+    }
+
+    protected void assertListenerStatus() {
+        assertTrue(testListener.isCompleted(DELAY));
+        ((JunctionTestListenerStatus) testListenerStatus).assertListenerStatus();
     }
 }
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
index cd4ac59..c2354e9 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
@@ -181,11 +181,9 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
 
         final Account account = createAccount(32);
 
-        final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
-        blockingStates.add(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE,  DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
-        blockingStates.add(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)));
+        blockingStateDao.setBlockingState(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE,  DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)), clock, internalCallContext);
+        blockingStateDao.setBlockingState(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)), clock, internalCallContext);
 
-        ((MockBlockingStateDao) blockingStateDao).setBlockingStates(bunId, blockingStates);
         final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext);
 
         Assert.assertEquals(events.size(), 3);
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
index 409ce5f..71a5b22 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
@@ -16,6 +16,22 @@
 
 package com.ning.billing.junction.plumbing.billing;
 
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.mockito.Mockito;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
 import com.ning.billing.account.api.Account;
 import com.ning.billing.catalog.MockPlan;
 import com.ning.billing.catalog.MockPlanPhase;
@@ -23,31 +39,16 @@ import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
 import com.ning.billing.entitlement.dao.MockBlockingStateDao;
-import com.ning.billing.junction.JunctionTestSuiteNoDB;
-import com.ning.billing.entitlement.api.BlockingState;
-import com.ning.billing.junction.plumbing.billing.BlockingCalculator.DisabledDuration;
-import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
-import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.junction.BillingEvent;
 import com.ning.billing.junction.BillingModeType;
 import com.ning.billing.junction.DefaultBlockingState;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.joda.time.LocalDate;
-import org.mockito.Mockito;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.UUID;
+import com.ning.billing.junction.JunctionTestSuiteNoDB;
+import com.ning.billing.junction.plumbing.billing.BlockingCalculator.DisabledDuration;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -114,9 +115,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
 
         final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
         blockingStates.add(new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now));
-        blockingStates.add(new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE,  "test", false, false, false, now.plusDays(2)));
+        blockingStates.add(new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)));
 
-        setBlockingStates(bundleId1, blockingStates);
+        setBlockingStates(blockingStates);
 
         blockingCalculator.insertBlockingEvents(billingEvents, internalCallContext);
 
@@ -516,7 +517,6 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertNotNull(minus5andAHalf);
         assertEquals(minus5andAHalf.getEffectiveDate(), now.minusDays(6));
 
-
     }
 
     protected BillingEvent createRealEvent(final DateTime effectiveDate, final SubscriptionBase subscription) {
@@ -538,9 +538,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final DateTimeZone tz = DateTimeZone.UTC;
 
         return new DefaultBillingEvent(account, subscription, effectiveDate, plan, planPhase,
-                fixedPrice, recurringPrice, currency,
-                billingPeriod, billCycleDay, billingModeType,
-                description, totalOrdering, type, tz);
+                                       fixedPrice, recurringPrice, currency,
+                                       billingPeriod, billCycleDay, billingModeType,
+                                       description, totalOrdering, type, tz);
     }
 
     @Test(groups = "fast")
@@ -610,7 +610,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
 
         public MockBillingEvent() {
             super(account, subscription1, clock.getUTCNow(), null, null, BigDecimal.ZERO, BigDecimal.TEN, Currency.USD, BillingPeriod.ANNUAL,
-                    4, BillingModeType.IN_ADVANCE, "", 3L, SubscriptionBaseTransitionType.CREATE, DateTimeZone.UTC);
+                  4, BillingModeType.IN_ADVANCE, "", 3L, SubscriptionBaseTransitionType.CREATE, DateTimeZone.UTC);
         }
     }
 
@@ -642,9 +642,10 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     public void testCreateDisablePairs() {
         List<BlockingState> blockingEvents;
         final UUID ovdId = UUID.randomUUID();
+        final UUID ovdId2 = UUID.randomUUID();
         final DateTime now = clock.getUTCNow();
 
-        //simple events open clear -> disabled
+        // Simple events open clear -> disabled
         blockingEvents = new ArrayList<BlockingState>();
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now));
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
@@ -655,7 +656,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertEquals(pairs.get(0).getStart(), now.plusDays(1));
         assertNull(pairs.get(0).getEnd());
 
-        //simple events closed clear -> disabled
+        // Simple events closed clear -> disabled
         blockingEvents = new ArrayList<BlockingState>();
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now));
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
@@ -668,7 +669,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertNotNull(pairs.get(0).getEnd());
         assertEquals(pairs.get(0).getEnd(), now.plusDays(2));
 
-        //simple BUNDLE events closed clear -> disabled
+        // Simple BUNDLE events closed clear -> disabled
         blockingEvents = new ArrayList<BlockingState>();
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now));
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
@@ -681,7 +682,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertNotNull(pairs.get(0).getEnd());
         assertEquals(pairs.get(0).getEnd(), now.plusDays(2));
 
-        //two or more disableds in a row
+        // Two or more disabled in a row
         blockingEvents = new ArrayList<BlockingState>();
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now));
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
@@ -708,6 +709,30 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertEquals(pairs.get(0).getStart(), now.plusDays(1));
         assertNotNull(pairs.get(0).getEnd());
         assertEquals(pairs.get(0).getEnd(), now.plusDays(4));
+
+        // Verify ordering at the same effective date doesn't matter. This is to work around nondeterministic ordering
+        // behavior in ProxyBlockingStateDao#BLOCKING_STATE_ORDERING_WITH_TIES_UNHANDLED. See also TestDefaultInternalBillingApi.
+        blockingEvents = new ArrayList<BlockingState>();
+        blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
+        blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)));
+        blockingEvents.add(new DefaultBlockingState(ovdId2, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(2)));
+        blockingEvents.add(new DefaultBlockingState(ovdId2, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(3)));
+
+        pairs = blockingCalculator.createBlockingDurations(blockingEvents);
+        assertEquals(pairs.size(), 1);
+        assertEquals(pairs.get(0).getStart(), now.plusDays(1));
+        assertEquals(pairs.get(0).getEnd(), now.plusDays(3));
+
+        blockingEvents = new ArrayList<BlockingState>();
+        blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
+        blockingEvents.add(new DefaultBlockingState(ovdId2, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(2)));
+        blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)));
+        blockingEvents.add(new DefaultBlockingState(ovdId2, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(3)));
+
+        pairs = blockingCalculator.createBlockingDurations(blockingEvents);
+        assertEquals(pairs.size(), 1);
+        assertEquals(pairs.get(0).getStart(), now.plusDays(1));
+        assertEquals(pairs.get(0).getEnd(), now.plusDays(3));
     }
 
     @Test(groups = "fast")
@@ -728,7 +753,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC)));
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC)));
 
-        setBlockingStates(bundleId1, blockingEvents);
+        setBlockingStates(blockingEvents);
 
         blockingCalculator.insertBlockingEvents(billingEvents, internalCallContext);
 
@@ -746,7 +771,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertEquals(events.get(4).getTransitionType(), SubscriptionBaseTransitionType.CHANGE);
     }
 
-    private void setBlockingStates(final UUID blockedId, final List<BlockingState> blockingStates) {
-        ((MockBlockingStateDao) blockingStateDao).setBlockingStates(blockedId, blockingStates);
+    private void setBlockingStates(final List<BlockingState> blockingStates) {
+        for (final BlockingState blockingState : blockingStates) {
+            blockingStateDao.setBlockingState(blockingState, clock, internalCallContext);
+        }
     }
 }
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
new file mode 100644
index 0000000..e99269a
--- /dev/null
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
@@ -0,0 +1,269 @@
+/*
+ * 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.junction.plumbing.billing;
+
+import java.util.List;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.callcontext.InternalCallContext;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.EntitlementService;
+import com.ning.billing.entitlement.api.BlockingStateType;
+import com.ning.billing.entitlement.api.DefaultEntitlementApi;
+import com.ning.billing.entitlement.api.Entitlement;
+import com.ning.billing.junction.BillingEvent;
+import com.ning.billing.junction.DefaultBlockingState;
+import com.ning.billing.junction.JunctionTestSuiteWithEmbeddedDB;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbeddedDB {
+
+    // See https://github.com/killbill/killbill/issues/123
+    //
+    // Pierre, why do we have invocationCount > 0 here?
+    //
+    // This test will exercise ProxyBlockingStateDao#BLOCKING_STATE_ORDERING_WITH_TIES_UNHANDLED - unfortunately, for some reason,
+    // the ordering doesn't seem deterministic. In some scenarii,
+    // BlockingState(idA, effectiveDate1, BLOCK), BlockingState(idA, effectiveDate2, CLEAR), BlockingState(idB, effectiveDate2, BLOCK), BlockingState(idB, effectiveDate3, CLEAR)
+    // is ordered
+    // BlockingState(idA, effectiveDate1, BLOCK), BlockingState(idB, effectiveDate2, BLOCK), BlockingState(idA, effectiveDate2, CLEAR), BlockingState(idB, effectiveDate3, CLEAR)
+    // The code BlockingCalculator#createBlockingDurations has been updated to support it, but we still want to make sure it actually works in both scenarii
+    // (invocationCount = 10 will trigger both usecases in my testing).
+    @Test(groups = "slow", description = "Check blocking states with same effective date are correctly handled", invocationCount = 10)
+    public void testBlockingStatesWithSameEffectiveDate() throws Exception {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+
+        testListener.pushExpectedEvent(NextEvent.CREATE);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), initialDate, callContext);
+        final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(entitlement.getId(), internalCallContext);
+        assertListenerStatus();
+
+        final DateTime block1Date = clock.getUTCNow();
+        testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
+        final DefaultBlockingState state1 = new DefaultBlockingState(account.getId(),
+                                                                     BlockingStateType.ACCOUNT,
+                                                                     DefaultEntitlementApi.ENT_STATE_BLOCKED,
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     true,
+                                                                     true,
+                                                                     true,
+                                                                     block1Date);
+        blockingInternalApi.setBlockingState(state1, internalCallContext);
+        // Same date, we'll order by record id asc
+        final DefaultBlockingState state2 = new DefaultBlockingState(account.getId(),
+                                                                     BlockingStateType.ACCOUNT,
+                                                                     DefaultEntitlementApi.ENT_STATE_CLEAR,
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     false,
+                                                                     false,
+                                                                     false,
+                                                                     block1Date);
+        blockingInternalApi.setBlockingState(state2, internalCallContext);
+        assertListenerStatus();
+
+        clock.addDays(5);
+
+        final DateTime block2Date = clock.getUTCNow();
+        testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
+        final DefaultBlockingState state3 = new DefaultBlockingState(entitlement.getBundleId(),
+                                                                     BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                                     DefaultEntitlementApi.ENT_STATE_BLOCKED,
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     true,
+                                                                     true,
+                                                                     true,
+                                                                     block2Date);
+        blockingInternalApi.setBlockingState(state3, internalCallContext);
+        // Same date, we'll order by record id asc
+        final DefaultBlockingState state4 = new DefaultBlockingState(entitlement.getBundleId(),
+                                                                     BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                                     DefaultEntitlementApi.ENT_STATE_CLEAR,
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     false,
+                                                                     false,
+                                                                     false,
+                                                                     block2Date);
+        blockingInternalApi.setBlockingState(state4, internalCallContext);
+        assertListenerStatus();
+
+        final DateTime block3Date = block2Date.plusDays(3);
+
+        // Pass the phase
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        clock.addDays(50);
+        assertListenerStatus();
+
+        final DateTime block4Date = clock.getUTCNow();
+        final DateTime block5Date = block4Date.plusDays(3);
+        // Only one event on the bus (for state5)
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        // Insert the clear state first, to make sure the order in which we insert blocking states doesn't matter
+        // Since we are already in an ENT_STATE_CLEAR state for service ENTITLEMENT_SERVICE_NAME, we need to use a different
+        // state name to simulate this behavior (otherwise, by design, this event won't be created)
+        final DefaultBlockingState state6 = new DefaultBlockingState(entitlement.getBundleId(),
+                                                                     BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                                     DefaultEntitlementApi.ENT_STATE_CLEAR + "-something",
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     false,
+                                                                     false,
+                                                                     false,
+                                                                     block5Date);
+        blockingInternalApi.setBlockingState(state6, internalCallContext);
+        final DefaultBlockingState state5 = new DefaultBlockingState(entitlement.getBundleId(),
+                                                                     BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                                     DefaultEntitlementApi.ENT_STATE_BLOCKED + "-something",
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     true,
+                                                                     true,
+                                                                     true,
+                                                                     block4Date);
+        blockingInternalApi.setBlockingState(state5, internalCallContext);
+        assertListenerStatus();
+
+        // Now, add back blocking states at an earlier date, for a different blockable id, to make sure the effective
+        // date ordering is correctly respected when computing blocking durations
+        testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
+        final DefaultBlockingState state7 = new DefaultBlockingState(account.getId(),
+                                                                     BlockingStateType.ACCOUNT,
+                                                                     DefaultEntitlementApi.ENT_STATE_BLOCKED + "-something2",
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     true,
+                                                                     true,
+                                                                     true,
+                                                                     block3Date);
+        blockingInternalApi.setBlockingState(state7, internalCallContext);
+        final DefaultBlockingState state8 = new DefaultBlockingState(account.getId(),
+                                                                     BlockingStateType.ACCOUNT,
+                                                                     DefaultEntitlementApi.ENT_STATE_CLEAR + "-something2",
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     false,
+                                                                     false,
+                                                                     false,
+                                                                     block4Date);
+        blockingInternalApi.setBlockingState(state8, internalCallContext);
+        assertListenerStatus();
+
+        // Advance for state6 to be active
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        clock.addDays(5);
+        assertListenerStatus();
+
+        // Expected blocking durations:
+        // * 2013-08-07 to 2013-08-07 (block1Date)
+        // * 2013-08-12 to 2013-08-12 (block2Date)
+        // * 2013-08-15 to 2013-10-04 [2013-08-15 to 2013-10-01 (block3Date -> block4Date) and 2013-10-01 to 2013-10-04 (block4Date -> block5Date)]
+        final List<BillingEvent> events = ImmutableList.<BillingEvent>copyOf(billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext));
+        Assert.assertEquals(events.size(), 7);
+        Assert.assertEquals(events.get(0).getTransitionType(), SubscriptionBaseTransitionType.CREATE);
+        Assert.assertEquals(events.get(0).getEffectiveDate(), subscription.getStartDate());
+        Assert.assertEquals(events.get(1).getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
+        Assert.assertEquals(events.get(1).getEffectiveDate(), block1Date);
+        Assert.assertEquals(events.get(2).getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
+        Assert.assertEquals(events.get(2).getEffectiveDate(), block1Date);
+        Assert.assertEquals(events.get(3).getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
+        Assert.assertEquals(events.get(3).getEffectiveDate(), block2Date);
+        Assert.assertEquals(events.get(4).getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
+        Assert.assertEquals(events.get(4).getEffectiveDate(), block2Date);
+        Assert.assertEquals(events.get(5).getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
+        Assert.assertEquals(events.get(5).getEffectiveDate(), block3Date);
+        Assert.assertEquals(events.get(6).getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
+        Assert.assertEquals(events.get(6).getEffectiveDate(), block5Date);
+    }
+
+    // See https://github.com/killbill/killbill/commit/92042843e38a67f75495b207385e4c1f9ca60990#commitcomment-4749967
+    @Test(groups = "slow", description = "Check unblock then block states with same effective date are correctly handled", invocationCount = 10)
+    public void testUnblockThenBlockBlockingStatesWithSameEffectiveDate() throws Exception {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+
+        testListener.pushExpectedEvent(NextEvent.CREATE);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), initialDate, callContext);
+        final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(entitlement.getId(), internalCallContext);
+        assertListenerStatus();
+
+        final DateTime block1Date = clock.getUTCNow();
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        final DefaultBlockingState state1 = new DefaultBlockingState(account.getId(),
+                                                                     BlockingStateType.ACCOUNT,
+                                                                     DefaultEntitlementApi.ENT_STATE_BLOCKED,
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     true,
+                                                                     true,
+                                                                     true,
+                                                                     block1Date);
+        blockingInternalApi.setBlockingState(state1, internalCallContext);
+
+        clock.addDays(1);
+
+        final DateTime block2Date = clock.getUTCNow();
+        testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
+        final DefaultBlockingState state2 = new DefaultBlockingState(account.getId(),
+                                                                     BlockingStateType.ACCOUNT,
+                                                                     DefaultEntitlementApi.ENT_STATE_CLEAR,
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     false,
+                                                                     false,
+                                                                     false,
+                                                                     block2Date);
+        blockingInternalApi.setBlockingState(state2, internalCallContext);
+        // Same date
+        final DefaultBlockingState state3 = new DefaultBlockingState(account.getId(),
+                                                                     BlockingStateType.ACCOUNT,
+                                                                     DefaultEntitlementApi.ENT_STATE_BLOCKED,
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     true,
+                                                                     true,
+                                                                     true,
+                                                                     block2Date);
+        blockingInternalApi.setBlockingState(state3, internalCallContext);
+        assertListenerStatus();
+
+        // Nothing should happen
+        clock.addDays(3);
+        assertListenerStatus();
+
+        // Expected blocking duration:
+        // * 2013-08-07 to now [2013-08-07 to 2013-08-08 then 2013-08-08 to now]
+        final List<BillingEvent> events = ImmutableList.<BillingEvent>copyOf(billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext));
+        Assert.assertEquals(events.size(), 2);
+        Assert.assertEquals(events.get(0).getTransitionType(), SubscriptionBaseTransitionType.CREATE);
+        Assert.assertEquals(events.get(0).getEffectiveDate(), subscription.getStartDate());
+        Assert.assertEquals(events.get(1).getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
+        Assert.assertEquals(events.get(1).getEffectiveDate(), block1Date);
+    }
+}
diff --git a/junction/src/test/resources/catalog.xml b/junction/src/test/resources/catalog.xml
new file mode 100644
index 0000000..973afd0
--- /dev/null
+++ b/junction/src/test/resources/catalog.xml
@@ -0,0 +1,828 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- ~ Copyright 2010-2011 Ning, Inc. ~ ~ Ning licenses this file to you 
+	under the Apache License, version 2.0 ~ (the "License"); you may not use 
+	this file except in compliance with the ~ License. You may obtain a copy 
+	of the License at: ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless 
+	required by applicable law or agreed to in writing, software ~ distributed 
+	under the License is distributed on an "AS IS" BASIS, WITHOUT ~ WARRANTIES 
+	OR CONDITIONS OF ANY KIND, either express or implied. See the ~ License for 
+	the specific language governing permissions and limitations ~ under the License. -->
+
+<!-- Use cases covered so far: Tiered Product (Pistol/Shotgun/Assault-Rifle) 
+	Multiple changeEvent plan policies Multiple PlanAlignment (see below, trial 
+	add-on alignments and rescue discount package) Product transition rules Add 
+	on (Scopes, Hoster) Multi-pack addon (Extra-Ammo) Addon Trial aligned to 
+	base plan (holster-monthly-regular) Addon Trial aligned to creation (holster-monthly-special) 
+	Rescue discount package (assault-rifle-annual-rescue) Plan phase with a reccurring 
+	and a one off (refurbish-maintenance) Phan with more than 2 phase (gunclub 
+	discount plans) Use Cases to do: Tiered Add On Riskfree period -->
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+
+	<effectiveDate>2011-01-01T00:00:00+00:00</effectiveDate>
+	<catalogName>Firearms</catalogName>
+
+	<currencies>
+		<currency>USD</currency>
+		<currency>EUR</currency>
+		<currency>GBP</currency>
+	</currencies>
+
+	<products>
+		<product name="Pistol">
+			<category>BASE</category>
+		</product>
+		<product name="Shotgun">
+			<category>BASE</category>
+			<available>
+				<addonProduct>Telescopic-Scope</addonProduct>
+				<addonProduct>Laser-Scope</addonProduct>
+			</available>
+		</product>
+		<product name="Assault-Rifle">
+			<category>BASE</category>
+			<included>
+				<addonProduct>Telescopic-Scope</addonProduct>
+			</included>
+			<available>
+				<addonProduct>Laser-Scope</addonProduct>
+			</available>
+		</product>
+		<product name="Telescopic-Scope">
+			<category>ADD_ON</category>
+		</product>
+		<product name="Laser-Scope">
+			<category>ADD_ON</category>
+		</product>
+		<product name="Holster">
+			<category>ADD_ON</category>
+		</product>
+		<product name="Extra-Ammo">
+			<category>ADD_ON</category>
+		</product>
+		<product name="Refurbish-Maintenance">
+			<category>ADD_ON</category>
+		</product>
+	</products>
+
+	<rules>
+		<changePolicy>
+			<changePolicyCase>
+				<phaseType>TRIAL</phaseType>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>
+                       <changePolicyCase>
+                                <toPriceList>rescue</toPriceList>
+                                <policy>END_OF_TERM</policy>
+                        </changePolicyCase>
+			<changePolicyCase>
+				<toProduct>Assault-Rifle</toProduct>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>
+			<changePolicyCase>
+				<fromProduct>Pistol</fromProduct>
+				<toProduct>Shotgun</toProduct>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>
+			<changePolicyCase>
+				<fromBillingPeriod>MONTHLY</fromBillingPeriod>
+				<toBillingPeriod>ANNUAL</toBillingPeriod>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>
+			<changePolicyCase>
+				<fromBillingPeriod>ANNUAL</fromBillingPeriod>
+				<toBillingPeriod>MONTHLY</toBillingPeriod>
+				<policy>END_OF_TERM</policy>
+			</changePolicyCase>
+			<changePolicyCase>
+				<policy>END_OF_TERM</policy>
+			</changePolicyCase>
+		</changePolicy>
+		<changeAlignment>
+			<changeAlignmentCase>
+				<toPriceList>rescue</toPriceList>
+				<alignment>CHANGE_OF_PLAN</alignment>
+			</changeAlignmentCase>
+			<changeAlignmentCase>
+				<fromPriceList>rescue</fromPriceList>
+				<toPriceList>rescue</toPriceList>
+				<alignment>CHANGE_OF_PRICELIST</alignment>
+			</changeAlignmentCase>
+			<changeAlignmentCase>
+				<alignment>START_OF_SUBSCRIPTION</alignment>
+			</changeAlignmentCase>
+		</changeAlignment>
+		<cancelPolicy>
+			<cancelPolicyCase>
+				<phaseType>TRIAL</phaseType>
+				<policy>IMMEDIATE</policy>
+			</cancelPolicyCase>
+			<cancelPolicyCase>
+				<policy>END_OF_TERM</policy>
+			</cancelPolicyCase>
+		</cancelPolicy>
+		<createAlignment>
+			<createAlignmentCase>
+				<product>Laser-Scope</product>
+				<alignment>START_OF_SUBSCRIPTION</alignment>
+			</createAlignmentCase>
+			<createAlignmentCase>
+				<alignment>START_OF_BUNDLE</alignment>
+			</createAlignmentCase>
+		</createAlignment>
+		<billingAlignment>
+			<billingAlignmentCase>
+				<productCategory>ADD_ON</productCategory>
+				<alignment>BUNDLE</alignment>
+			</billingAlignmentCase>
+			<billingAlignmentCase>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<alignment>SUBSCRIPTION</alignment>
+			</billingAlignmentCase>
+			<billingAlignmentCase>
+				<alignment>ACCOUNT</alignment>
+			</billingAlignmentCase>
+		</billingAlignment>
+		<priceList>
+			<priceListCase>
+				<fromPriceList>rescue</fromPriceList>
+				<toPriceList>DEFAULT</toPriceList>
+			</priceListCase>
+		</priceList>
+	</rules>
+
+	<plans>
+		<plan name="pistol-monthly">
+			<product>Pistol</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice> <!-- empty price implies $0 -->
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>GBP</currency>
+						<value>29.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>29.95</value>
+					</price>
+					<price>
+						<currency>USD</currency>
+						<value>29.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="shotgun-monthly">
+			<product>Shotgun</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice></fixedPrice>
+					<!-- no price implies $0 -->
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+					<number>-1</number>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>249.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>149.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>169.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="assault-rifle-monthly">
+			<product>Assault-Rifle</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>599.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>349.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>399.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="pistol-annual">
+			<product>Pistol</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="shotgun-annual">
+			<product>Shotgun</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>2399.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>1499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>1699.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="assault-rifle-annual">
+			<product>Assault-Rifle</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>5999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>3499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>3999.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="pistol-annual-gunclub-discount">
+			<product>Pistol</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>6</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>9.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>9.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>9.95</value>
+						</price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="shotgun-annual-gunclub-discount">
+			<product>Shotgun</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>6</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>19.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>49.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>69.95</value>
+						</price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>2399.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>1499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>1699.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="assault-rifle-annual-gunclub-discount">
+			<product>Assault-Rifle</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>6</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>99.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>99.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>99.95</value>
+						</price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>5999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>3499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>3999.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="laser-scope-monthly">
+			<product>Laser-Scope</product>
+			<initialPhases>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>1</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>999.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>499.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>999.95</value>
+						</price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>1999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>1499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>1999.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="telescopic-scope-monthly">
+			<product>Telescopic-Scope</product>
+			<initialPhases>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>1</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>399.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>299.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>399.95</value>
+						</price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>999.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="extra-ammo-monthly">
+			<product>Extra-Ammo</product>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>999.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+			<plansAllowedInBundle>-1</plansAllowedInBundle> <!-- arbitrary number of these (multipack) -->
+		</plan>
+		<plan name="holster-monthly-regular">
+			<product>Holster</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="holster-monthly-special">
+			<product>Holster</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="assault-rifle-annual-rescue">
+			<product>Assault-Rifle</product>
+			<initialPhases>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>YEARS</unit>
+						<number>1</number>
+					</duration>
+					<billingPeriod>ANNUAL</billingPeriod>
+					<recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>5999.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>3499.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>3999.95</value>
+						</price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>5999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>3499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>3999.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="refurbish-maintenance">
+			<product>Refurbish-Maintenance</product>
+			<finalPhase type="FIXEDTERM">
+				<duration>
+					<unit>MONTHS</unit>
+					<number>12</number>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
+				</recurringPrice>
+				<fixedPrice>
+					<price>
+						<currency>USD</currency>
+						<value>599.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>599.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>599.95</value>
+					</price>
+				</fixedPrice>
+			</finalPhase>
+		</plan>
+	</plans>
+	<priceLists>
+		<defaultPriceList name="DEFAULT">
+			<plans>
+				<plan>pistol-monthly</plan>
+				<plan>shotgun-monthly</plan>
+				<plan>assault-rifle-monthly</plan>
+				<plan>pistol-annual</plan>
+				<plan>shotgun-annual</plan>
+				<plan>assault-rifle-annual</plan>
+				<plan>laser-scope-monthly</plan>
+				<plan>telescopic-scope-monthly</plan>
+				<plan>extra-ammo-monthly</plan>
+				<plan>holster-monthly-regular</plan>
+				<plan>refurbish-maintenance</plan>
+			</plans>
+		</defaultPriceList>
+		<childPriceList name="gunclubDiscount">
+			<plans>
+				<plan>pistol-monthly</plan>
+				<plan>shotgun-monthly</plan>
+				<plan>assault-rifle-monthly</plan>
+				<plan>pistol-annual-gunclub-discount</plan>
+				<plan>shotgun-annual-gunclub-discount</plan>
+				<plan>assault-rifle-annual-gunclub-discount</plan>
+				<plan>holster-monthly-special</plan>
+			</plans>
+		</childPriceList>
+		<childPriceList name="rescue">
+			<plans>
+				<plan>assault-rifle-annual-rescue</plan>
+			</plans>
+		</childPriceList>
+	</priceLists>
+
+</catalog>

NEWS 16(+16 -0)

diff --git a/NEWS b/NEWS
index c116256..3a050f2 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,19 @@
+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
+    Fix ClassCastException in Beatrix
+    https://github.com/killbill/killbill/issues/123
+    https://github.com/killbill/killbill/issues/132
+
+0.8.3
+    Entitlement speedups
+
 0.8.2
     Lots of subscription & entitlement bugfixes
     https://github.com/killbill/killbill/issues/121

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

diff --git a/osgi/pom.xml b/osgi/pom.xml
index 8dec480..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.3-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 31b2830..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.3-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 906e35b..f5fc4b1 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.3-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 4f8c8cc..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.3-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 b5133e7..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.3-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 a72ee5f..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.3-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 267a987..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.3-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 1bb6ae2..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.3-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 010af7f..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.3-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 a3a5e6b..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.3-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 cf066e8..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.3-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 157f015..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.3-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 d629b01..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.3-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 00f74f3..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.3-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-overdue</artifactId>
diff --git a/overdue/src/test/java/com/ning/billing/overdue/glue/ApplicatorMockJunctionModule.java b/overdue/src/test/java/com/ning/billing/overdue/glue/ApplicatorMockJunctionModule.java
index 12625ab..1d4bf63 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/glue/ApplicatorMockJunctionModule.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/glue/ApplicatorMockJunctionModule.java
@@ -54,7 +54,7 @@ public class ApplicatorMockJunctionModule extends AbstractModule {
         }
 
         @Override
-        public List<BlockingState> getBlockingAll(final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
+        public List<BlockingState> getBlockingAllForAccount(final InternalTenantContext context) {
             throw new UnsupportedOperationException();
         }
 

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

diff --git a/payment/pom.xml b/payment/pom.xml
index 3823ba6..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.3-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 37bde5f..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
@@ -27,12 +27,11 @@ import javax.annotation.Nullable;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.entity.EntityBase;
 import com.ning.billing.payment.dao.PaymentAttemptModelDao;
 import com.ning.billing.payment.dao.PaymentModelDao;
 import com.ning.billing.payment.dao.RefundModelDao;
-import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
 import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
-import com.ning.billing.entity.EntityBase;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
@@ -159,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());
             }
         }
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentPluginErrorEvent.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentPluginErrorEvent.java
index db409b7..29372a2 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentPluginErrorEvent.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentPluginErrorEvent.java
@@ -65,7 +65,7 @@ public class DefaultPaymentPluginErrorEvent extends BusEventBase implements Paym
     @JsonIgnore
     @Override
     public BusInternalEventType getBusEventType() {
-        return BusInternalEventType.PAYMENT_ERROR;
+        return BusInternalEventType.PAYMENT_PLUGIN_ERROR;
     }
 
     @Override
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultRefund.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultRefund.java
index 54f99e0..9349504 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultRefund.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultRefund.java
@@ -24,8 +24,8 @@ import javax.annotation.Nullable;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
 import com.ning.billing.entity.EntityBase;
+import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
 
 public class DefaultRefund extends EntityBase implements Refund {
 
@@ -34,16 +34,18 @@ public class DefaultRefund extends EntityBase implements Refund {
     private final Currency currency;
     private final boolean isAdjusted;
     private final DateTime effectiveDate;
+    private final RefundStatus refundStatus;
 
     public DefaultRefund(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate,
                          final UUID paymentId, final BigDecimal amount,
-                         final Currency currency, final boolean isAdjusted, final DateTime effectiveDate) {
+                         final Currency currency, final boolean isAdjusted, final DateTime effectiveDate, final RefundStatus refundStatus) {
         super(id, createdDate, updatedDate);
         this.paymentId = paymentId;
         this.amount = amount;
         this.currency = currency;
         this.isAdjusted = isAdjusted;
         this.effectiveDate = effectiveDate;
+        this.refundStatus = refundStatus;
     }
 
     @Override
@@ -72,6 +74,11 @@ public class DefaultRefund extends EntityBase implements Refund {
     }
 
     @Override
+    public RefundStatus getRefundStatus() {
+        return refundStatus;
+    }
+
+    @Override
     public RefundInfoPlugin getPluginDetail() {
         // TODO not implemented
         return null;
@@ -85,6 +92,7 @@ public class DefaultRefund extends EntityBase implements Refund {
         sb.append(", amount=").append(amount);
         sb.append(", currency=").append(currency);
         sb.append(", isAdjusted=").append(isAdjusted);
+        sb.append(", status=").append(refundStatus);
         sb.append(", effectiveDate=").append(effectiveDate);
         sb.append('}');
         return sb.toString();
@@ -113,6 +121,9 @@ public class DefaultRefund extends EntityBase implements Refund {
         if (currency != that.currency) {
             return false;
         }
+        if (refundStatus != that.refundStatus) {
+            return false;
+        }
         if (paymentId != null ? !paymentId.equals(that.paymentId) : that.paymentId != null) {
             return false;
         }
@@ -125,6 +136,7 @@ public class DefaultRefund extends EntityBase implements Refund {
         int result = paymentId != null ? paymentId.hashCode() : 0;
         result = 31 * result + (amount != null ? amount.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (refundStatus != null ? refundStatus.hashCode() : 0);
         result = 31 * result + (isAdjusted ? 1 : 0);
         result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
         return result;
diff --git a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
index 7eab0e9..a3fe237 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
@@ -43,10 +43,10 @@ import com.ning.billing.osgi.api.OSGIServiceRegistration;
 import com.ning.billing.payment.api.DefaultRefund;
 import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.Refund;
+import com.ning.billing.payment.api.RefundStatus;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.dao.PaymentModelDao;
 import com.ning.billing.payment.dao.RefundModelDao;
-import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
 import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
 import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
@@ -136,7 +136,7 @@ public class RefundProcessor extends ProcessorBase {
 
                         return new DefaultRefund(refundInfo.getId(), refundInfo.getCreatedDate(), refundInfo.getUpdatedDate(),
                                                  paymentId, refundInfo.getAmount(), account.getCurrency(),
-                                                 isAdjusted, refundInfo.getCreatedDate());
+                                                 isAdjusted, refundInfo.getCreatedDate(), RefundStatus.COMPLETED);
                     } else {
                         paymentDao.updateRefundStatus(refundInfo.getId(), RefundStatus.PLUGIN_ERRORED, refundAmount, account.getCurrency(), context);
                         throw new PaymentPluginApiException("Refund error for RefundInfo: " + refundInfo.toString(),
@@ -208,7 +208,7 @@ public class RefundProcessor extends ProcessorBase {
         }
         return new DefaultRefund(result.getId(), result.getCreatedDate(), result.getUpdatedDate(),
                                  result.getPaymentId(), result.getAmount(), result.getCurrency(),
-                                 result.isAdjusted(), result.getCreatedDate());
+                                 result.isAdjusted(), result.getCreatedDate(), result.getRefundStatus());
     }
 
     public List<Refund> getAccountRefunds(final Account account, final InternalTenantContext context)
@@ -237,7 +237,7 @@ public class RefundProcessor extends ProcessorBase {
             public Refund apply(final RefundModelDao cur) {
                 return new DefaultRefund(cur.getId(), cur.getCreatedDate(), cur.getUpdatedDate(),
                                          cur.getPaymentId(), cur.getAmount(), cur.getCurrency(),
-                                         cur.isAdjusted(), cur.getCreatedDate());
+                                         cur.isAdjusted(), cur.getCreatedDate(), cur.getRefundStatus());
             }
         }));
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
index ca5509a..ccc0e17 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
@@ -32,7 +32,7 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.clock.Clock;
 import com.ning.billing.entity.EntityPersistenceException;
 import com.ning.billing.payment.api.PaymentStatus;
-import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
+import com.ning.billing.payment.api.RefundStatus;
 import com.ning.billing.util.cache.CacheControllerDispatcher;
 import com.ning.billing.util.dao.NonEntityDao;
 import com.ning.billing.util.entity.DefaultPagination;
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptModelDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptModelDao.java
index 058288c..1565e9b 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptModelDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptModelDao.java
@@ -119,6 +119,46 @@ public class PaymentAttemptModelDao extends EntityBase implements EntityModelDao
         return requestedCurrency;
     }
 
+    public void setAccountId(final UUID accountId) {
+        this.accountId = accountId;
+    }
+
+    public void setInvoiceId(final UUID invoiceId) {
+        this.invoiceId = invoiceId;
+    }
+
+    public void setPaymentId(final UUID paymentId) {
+        this.paymentId = paymentId;
+    }
+
+    public void setPaymentMethodId(final UUID paymentMethodId) {
+        this.paymentMethodId = paymentMethodId;
+    }
+
+    public void setProcessingStatus(final PaymentStatus processingStatus) {
+        this.processingStatus = processingStatus;
+    }
+
+    public void setEffectiveDate(final DateTime effectiveDate) {
+        this.effectiveDate = effectiveDate;
+    }
+
+    public void setGatewayErrorCode(final String gatewayErrorCode) {
+        this.gatewayErrorCode = gatewayErrorCode;
+    }
+
+    public void setGatewayErrorMsg(final String gatewayErrorMsg) {
+        this.gatewayErrorMsg = gatewayErrorMsg;
+    }
+
+    public void setRequestedAmount(final BigDecimal requestedAmount) {
+        this.requestedAmount = requestedAmount;
+    }
+
+    public void setRequestedCurrency(final Currency requestedCurrency) {
+        this.requestedCurrency = requestedCurrency;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
index 575e6ca..2c2e76e 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
@@ -20,13 +20,11 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
-
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.payment.api.PaymentStatus;
-import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
 import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.payment.api.RefundStatus;
 import com.ning.billing.util.entity.Pagination;
 
 public interface PaymentDao {
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentModelDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentModelDao.java
index f7fa1e9..9b8a4f9 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentModelDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentModelDao.java
@@ -131,6 +131,54 @@ public class PaymentModelDao extends EntityBase implements EntityModelDao<Paymen
         return extSecondPaymentRefId;
     }
 
+    public void setAccountId(final UUID accountId) {
+        this.accountId = accountId;
+    }
+
+    public void setInvoiceId(final UUID invoiceId) {
+        this.invoiceId = invoiceId;
+    }
+
+    public void setPaymentMethodId(final UUID paymentMethodId) {
+        this.paymentMethodId = paymentMethodId;
+    }
+
+    public void setAmount(final BigDecimal amount) {
+        this.amount = amount;
+    }
+
+    public void setCurrency(final Currency currency) {
+        this.currency = currency;
+    }
+
+    public void setProcessedAmount(final BigDecimal processedAmount) {
+        this.processedAmount = processedAmount;
+    }
+
+    public void setProcessedCurrency(final Currency processedCurrency) {
+        this.processedCurrency = processedCurrency;
+    }
+
+    public void setEffectiveDate(final DateTime effectiveDate) {
+        this.effectiveDate = effectiveDate;
+    }
+
+    public void setPaymentNumber(final Integer paymentNumber) {
+        this.paymentNumber = paymentNumber;
+    }
+
+    public void setPaymentStatus(final PaymentStatus paymentStatus) {
+        this.paymentStatus = paymentStatus;
+    }
+
+    public void setExtFirstPaymentRefId(final String extFirstPaymentRefId) {
+        this.extFirstPaymentRefId = extFirstPaymentRefId;
+    }
+
+    public void setExtSecondPaymentRefId(final String extSecondPaymentRefId) {
+        this.extSecondPaymentRefId = extSecondPaymentRefId;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java b/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java
index eb4d052..176fe0c 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java
@@ -24,9 +24,10 @@ import javax.annotation.Nullable;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.entity.EntityBase;
 import com.ning.billing.payment.api.Refund;
+import com.ning.billing.payment.api.RefundStatus;
 import com.ning.billing.util.dao.TableName;
-import com.ning.billing.entity.EntityBase;
 import com.ning.billing.util.entity.dao.EntityModelDao;
 
 public class RefundModelDao extends EntityBase implements EntityModelDao<Refund> {
@@ -100,11 +101,36 @@ public class RefundModelDao extends EntityBase implements EntityModelDao<Refund>
         return isAdjusted;
     }
 
-    public enum RefundStatus {
-        CREATED,
-        PLUGIN_COMPLETED,
-        COMPLETED,
-        PLUGIN_ERRORED
+    public void setAccountId(final UUID accountId) {
+        this.accountId = accountId;
+    }
+
+    public void setPaymentId(final UUID paymentId) {
+        this.paymentId = paymentId;
+    }
+
+    public void setAmount(final BigDecimal amount) {
+        this.amount = amount;
+    }
+
+    public void setCurrency(final Currency currency) {
+        this.currency = currency;
+    }
+
+    public void setProcessedAmount(final BigDecimal processedAmount) {
+        this.processedAmount = processedAmount;
+    }
+
+    public void setProcessedCurrency(final Currency processedCurrency) {
+        this.processedCurrency = processedCurrency;
+    }
+
+    public void setIsAdjusted(final boolean isAdjusted) {
+        this.isAdjusted = isAdjusted;
+    }
+
+    public void setRefundStatus(final RefundStatus refundStatus) {
+        this.refundStatus = refundStatus;
     }
 
     @Override
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
index 15c73bb..17692e2 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
@@ -56,7 +56,7 @@ where pa.payment_id = :paymentId
 and p.id = :paymentId
 <AND_CHECK_TENANT("pa.")>
 <AND_CHECK_TENANT("p.")>
-order by effective_date asc
+<defaultOrderBy("pa.")>
 ;
 >>
 
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
index 6ad9b50..871d641 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
@@ -5,6 +5,10 @@ extraTableFieldsWithComma(prefix) ::= <<
 , <prefix>record_id as payment_number
 >>
 
+defaultOrderBy(prefix) ::= <<
+order by <prefix>effective_date ASC, <recordIdField(prefix)> ASC
+>>
+
 tableFields(prefix) ::= <<
   <prefix>account_id
 , <prefix>invoice_id
@@ -48,6 +52,7 @@ select <allTableFields()>
 from payments
 where account_id = :accountId
 <AND_CHECK_TENANT()>
+<defaultOrderBy()>
 ;
 >>
 
@@ -57,6 +62,7 @@ select <allTableFields()>
 from payments
 where invoice_id = :invoiceId
 <AND_CHECK_TENANT()>
+<defaultOrderBy()>
 ;
 >>
 
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg
index aa8daa6..4efb97d 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg
@@ -49,6 +49,7 @@ select <allTableFields()>
 from <tableName()>
 where payment_id = :paymentId
 <AND_CHECK_TENANT()>
+<defaultOrderBy()>
 ;
 >>
 
@@ -57,5 +58,6 @@ select <allTableFields()>
 from <tableName()>
 where account_id = :accountId
 <AND_CHECK_TENANT()>
+<defaultOrderBy()>
 ;
 >>
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
index fc75d31..2a245c6 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
@@ -30,7 +30,7 @@ import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.api.PaymentStatus;
-import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
+import com.ning.billing.payment.api.RefundStatus;
 import com.ning.billing.util.entity.Pagination;
 
 import com.google.common.collect.ImmutableList;
@@ -186,6 +186,7 @@ public class MockPaymentDao implements PaymentDao {
         return null;
     }
 
+
     @Override
     public void updateRefundStatus(final UUID refundId, final RefundStatus status, final BigDecimal processedAmount, final Currency processedCurrency, final InternalCallContext context) {
         return;
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
index 70033b8..49717e9 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
@@ -27,13 +27,12 @@ import org.testng.annotations.Test;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.PaymentTestSuiteWithEmbeddedDB;
 import com.ning.billing.payment.api.PaymentStatus;
-import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
+import com.ning.billing.payment.api.RefundStatus;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
 public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
@@ -205,7 +204,6 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(lastPayment.getEffectiveDate().compareTo(effectiveDate), 0);
         assertEquals(lastPayment.getPaymentStatus(), PaymentStatus.UNKNOWN);
 
-
         clock.addDays(3);
         final DateTime newEffectiveDate = clock.getUTCNow();
         final UUID newPaymentMethodId = UUID.randomUUID();

pom.xml 4(+2 -2)

diff --git a/pom.xml b/pom.xml
index d69541b..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.6</version>
+        <version>0.5.8</version>
     </parent>
     <artifactId>killbill</artifactId>
-    <version>0.8.3-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 4d079a1..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.3-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-server</artifactId>
diff --git a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
index baecbc6..5126a79 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
@@ -59,6 +59,7 @@ import com.ning.billing.jaxrs.json.RefundJson;
 import com.ning.billing.jaxrs.json.SubscriptionJson;
 import com.ning.billing.jaxrs.json.TenantJson;
 import com.ning.billing.jaxrs.resources.JaxrsResource;
+import com.ning.billing.payment.api.RefundStatus;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.http.client.AsyncCompletionHandler;
 import com.ning.http.client.AsyncHttpClient;
@@ -370,13 +371,11 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         assertEquals(fifthResponse.getStatusCode(), javax.ws.rs.core.Response.Status.OK.getStatusCode());
     }
 
-
-
     protected SubscriptionJson createEntitlement(final String accountId, final String bundleExternalKey, final String productName, final String productCategory, final String billingPeriod, final boolean waitCompletion) throws Exception {
 
-        final SubscriptionJson input =  new SubscriptionJson(accountId, null, null, bundleExternalKey, null ,productName, productCategory,
-                                                                            billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null, null, null, null, null,
-                                                                            null, null, null);
+        final SubscriptionJson input = new SubscriptionJson(accountId, null, null, bundleExternalKey, null, productName, productCategory,
+                                                            billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null, null, null, null, null,
+                                                            null, null, null);
         String baseJson = mapper.writeValueAsString(input);
 
         final Map<String, String> queryParams = waitCompletion ? getQueryParamsForCallCompletion("5") : DEFAULT_EMPTY_QUERY;
@@ -542,25 +541,25 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         }
 
         final InvoiceItemJson adjustment = new InvoiceItemJson(invoiceItemId, null, null, accountId, null, null, null, null,
-                                                                           null, null, null, null, amount, currency, null);
+                                                               null, null, null, null, amount, currency, null);
         final String adjustmentJson = mapper.writeValueAsString(adjustment);
         final Response response = doPost(uri, adjustmentJson, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
     }
 
     protected InvoiceJson createExternalCharge(final String accountId, final BigDecimal amount, @Nullable final String bundleId,
-                                                        @Nullable final Currency currency, @Nullable final DateTime requestedDate, final Boolean autoPay) throws Exception {
+                                               @Nullable final Currency currency, @Nullable final DateTime requestedDate, final Boolean autoPay) throws Exception {
         return doCreateExternalCharge(accountId, null, bundleId, amount, currency, requestedDate, autoPay, JaxrsResource.CHARGES_PATH);
     }
 
     protected InvoiceJson createExternalChargeForInvoice(final String accountId, final String invoiceId, @Nullable final String bundleId, final BigDecimal amount,
-                                                                  @Nullable final Currency currency, @Nullable final DateTime requestedDate, final Boolean autoPay) throws Exception {
+                                                         @Nullable final Currency currency, @Nullable final DateTime requestedDate, final Boolean autoPay) throws Exception {
         final String uri = JaxrsResource.INVOICES_PATH + "/" + invoiceId + "/" + JaxrsResource.CHARGES;
         return doCreateExternalCharge(accountId, invoiceId, bundleId, amount, currency, requestedDate, autoPay, uri);
     }
 
     private InvoiceJson doCreateExternalCharge(final String accountId, @Nullable final String invoiceId, @Nullable final String bundleId, @Nullable final BigDecimal amount,
-                                                        @Nullable final Currency currency, final DateTime requestedDate, final Boolean autoPay, final String uri) throws IOException {
+                                               @Nullable final Currency currency, final DateTime requestedDate, final Boolean autoPay, final String uri) throws IOException {
         final Map<String, String> queryParams = new HashMap<String, String>();
         if (requestedDate != null) {
             queryParams.put(JaxrsResource.QUERY_REQUESTED_DT, requestedDate.toDateTimeISO().toString());
@@ -570,7 +569,7 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         }
 
         final InvoiceItemJson externalCharge = new InvoiceItemJson(null, invoiceId, null, accountId, bundleId, null, null, null,
-                                                                               null, null, null, null, amount, currency, null);
+                                                                   null, null, null, null, amount, currency, null);
         final String externalChargeJson = mapper.writeValueAsString(externalCharge);
         final Response response = doPost(uri, externalChargeJson, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
@@ -691,7 +690,7 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
 
     protected void payAllInvoices(final AccountJson accountJson, final Boolean externalPayment) throws IOException {
         final PaymentJson payment = new PaymentJson(null, null, accountJson.getAccountId(), null, null, null, null,
-                                                                null, null, 0, null, null, null, null, null, null, null, null);
+                                                    null, null, 0, null, null, null, null, null, null, null, null);
         final String postJson = mapper.writeValueAsString(payment);
 
         final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.PAYMENTS;
@@ -700,7 +699,7 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
 
     protected List<PaymentJson> createInstaPayment(final AccountJson accountJson, final InvoiceJson invoice) throws IOException {
         final PaymentJson payment = new PaymentJson(invoice.getAmount(), BigDecimal.ZERO, accountJson.getAccountId(),
-                                                                invoice.getInvoiceId(), null, null, null, null, null, 0, null, null, null, null, null, null, null, null);
+                                                    invoice.getInvoiceId(), null, null, null, null, null, 0, null, null, null, null, null, null, null, null);
         final String postJson = mapper.writeValueAsString(payment);
 
         final String uri = JaxrsResource.INVOICES_PATH + "/" + invoice.getInvoiceId() + "/" + JaxrsResource.PAYMENTS;
@@ -711,8 +710,8 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
 
     protected List<PaymentJson> createExternalPayment(final AccountJson accountJson, final String invoiceId, final BigDecimal paidAmount) throws IOException {
         final PaymentJson payment = new PaymentJson(paidAmount, BigDecimal.ZERO, accountJson.getAccountId(),
-                                                                invoiceId, null, null, null, null, null, 0,
-                                                                null, null, null, null, null, null, null, null);
+                                                    invoiceId, null, null, null, null, null, 0,
+                                                    null, null, null, null, null, null, null, null);
         final String postJson = mapper.writeValueAsString(payment);
 
         final String paymentURI = JaxrsResource.INVOICES_PATH + "/" + invoiceId + "/" + JaxrsResource.PAYMENTS;
@@ -791,9 +790,9 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         final List<InvoiceItemJson> adjustments = new ArrayList<InvoiceItemJson>();
         for (final String itemId : itemAdjustments.keySet()) {
             adjustments.add(new InvoiceItemJson(itemId, null, null, null, null, null, null, null, null, null, null, null,
-                                                      itemAdjustments.get(itemId), null, null));
+                                                itemAdjustments.get(itemId), null, null));
         }
-        final RefundJson refundJson = new RefundJson(null, paymentId, amount, DEFAULT_CURRENCY, adjusted, null, null, adjustments, null);
+        final RefundJson refundJson = new RefundJson(null, paymentId, amount, DEFAULT_CURRENCY, RefundStatus.COMPLETED.toString(), adjusted, null, null, adjustments, null);
         final String baseJson = mapper.writeValueAsString(refundJson);
         final Response response = doPost(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
@@ -867,7 +866,6 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
     // OVERDUE
     //
 
-
     protected OverdueStateJson getOverdueStateForAccount(final String accountId) throws Exception {
         final String overdueURI = JaxrsResource.ACCOUNTS_PATH + "/" + accountId + "/" + OVERDUE;
         final Response overdueResponse = doGet(overdueURI, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java b/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
index 97201a2..b99874d 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
@@ -95,7 +95,7 @@ public class TestBundle extends TestJaxrsBase {
         queryParams.put(JaxrsResource.QUERY_EXTERNAL_KEY, "56566");
         uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.BUNDLES;
         response = doGet(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
-        Assert.assertEquals(response.getStatusCode(), Status.NOT_FOUND.getStatusCode());
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
 
         uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.BUNDLES;
         response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java b/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java
index 01200d6..321903c 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java
@@ -30,6 +30,7 @@ import com.ning.billing.jaxrs.json.InvoiceJson;
 import com.ning.billing.jaxrs.json.PaymentJson;
 import com.ning.billing.jaxrs.json.PaymentMethodJson;
 import com.ning.billing.jaxrs.json.RefundJson;
+import com.ning.billing.payment.api.RefundStatus;
 
 public class TestPayment extends TestJaxrsBase {
 
@@ -177,6 +178,7 @@ public class TestPayment extends TestJaxrsBase {
         Assert.assertEquals(refundJsonCheck.getPaymentId(), paymentJson.getPaymentId());
         Assert.assertEquals(refundJsonCheck.getAmount().setScale(2, RoundingMode.HALF_UP), refundAmount.setScale(2, RoundingMode.HALF_UP));
         Assert.assertEquals(refundJsonCheck.getCurrency(), DEFAULT_CURRENCY);
+        Assert.assertEquals(refundJsonCheck.getStatus(), RefundStatus.COMPLETED.toString());
         Assert.assertEquals(refundJsonCheck.getEffectiveDate().getYear(), clock.getUTCNow().getYear());
         Assert.assertEquals(refundJsonCheck.getEffectiveDate().getMonthOfYear(), clock.getUTCNow().getMonthOfYear());
         Assert.assertEquals(refundJsonCheck.getEffectiveDate().getDayOfMonth(), clock.getUTCNow().getDayOfMonth());
diff --git a/subscription/pom.xml b/subscription/pom.xml
index 8b8f285..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.3-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-subscription</artifactId>
diff --git a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 7aec7aa..21edeeb 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -39,10 +39,20 @@ import org.slf4j.LoggerFactory;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.bus.api.PersistentBus;
 import com.ning.billing.bus.api.PersistentBus.EventBusException;
+import com.ning.billing.callcontext.InternalCallContext;
+import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.clock.Clock;
+import com.ning.billing.entity.EntityPersistenceException;
+import com.ning.billing.events.EffectiveSubscriptionInternalEvent;
+import com.ning.billing.events.RepairSubscriptionInternalEvent;
+import com.ning.billing.notificationq.api.NotificationEvent;
+import com.ning.billing.notificationq.api.NotificationQueue;
+import com.ning.billing.notificationq.api.NotificationQueueService;
+import com.ning.billing.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
+import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.subscription.api.migration.AccountMigrationData;
 import com.ning.billing.subscription.api.migration.AccountMigrationData.BundleMigrationData;
 import com.ning.billing.subscription.api.migration.AccountMigrationData.SubscriptionMigrationData;
@@ -54,13 +64,13 @@ import com.ning.billing.subscription.api.user.DefaultRequestedSubscriptionEvent;
 import com.ning.billing.subscription.api.user.DefaultSubscriptionBase;
 import com.ning.billing.subscription.api.user.DefaultSubscriptionBaseBundle;
 import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
-import com.ning.billing.subscription.api.user.SubscriptionBuilder;
 import com.ning.billing.subscription.api.user.SubscriptionBaseTransitionData;
+import com.ning.billing.subscription.api.user.SubscriptionBuilder;
 import com.ning.billing.subscription.engine.addon.AddonUtils;
 import com.ning.billing.subscription.engine.core.DefaultSubscriptionBaseService;
 import com.ning.billing.subscription.engine.core.SubscriptionNotificationKey;
-import com.ning.billing.subscription.engine.dao.model.SubscriptionEventModelDao;
 import com.ning.billing.subscription.engine.dao.model.SubscriptionBundleModelDao;
+import com.ning.billing.subscription.engine.dao.model.SubscriptionEventModelDao;
 import com.ning.billing.subscription.engine.dao.model.SubscriptionModelDao;
 import com.ning.billing.subscription.events.SubscriptionBaseEvent;
 import com.ning.billing.subscription.events.SubscriptionBaseEvent.EventType;
@@ -72,27 +82,20 @@ import com.ning.billing.subscription.events.user.ApiEventChange;
 import com.ning.billing.subscription.events.user.ApiEventMigrateBilling;
 import com.ning.billing.subscription.events.user.ApiEventType;
 import com.ning.billing.subscription.exceptions.SubscriptionBaseError;
-import com.ning.billing.notificationq.api.NotificationEvent;
-import com.ning.billing.notificationq.api.NotificationQueue;
-import com.ning.billing.notificationq.api.NotificationQueueService;
-import com.ning.billing.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
-import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.util.cache.CacheControllerDispatcher;
-import com.ning.billing.callcontext.InternalCallContext;
-import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.util.dao.NonEntityDao;
-import com.ning.billing.entity.EntityPersistenceException;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
 import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
-import com.ning.billing.events.EffectiveSubscriptionInternalEvent;
-import com.ning.billing.events.RepairSubscriptionInternalEvent;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
+import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Collections2;
-
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
 
 public class DefaultSubscriptionDao implements SubscriptionDao {
 
@@ -236,10 +239,9 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         return buildSubscription(shellSubscription, context);
     }
 
-
     @Override
     public List<SubscriptionBase> getSubscriptions(final UUID bundleId, final InternalTenantContext context) {
-        return buildBundleSubscriptions(bundleId, getSubscriptionFromBundleId(bundleId, context), context);
+        return buildBundleSubscriptions(getSubscriptionFromBundleId(bundleId, context), null, context);
     }
 
     private List<SubscriptionBase> getSubscriptionFromBundleId(final UUID bundleId, final InternalTenantContext context) {
@@ -257,13 +259,37 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         });
     }
 
+
     @Override
     public Map<UUID, List<SubscriptionBase>> getSubscriptionsForAccount(final InternalTenantContext context) {
         final Map<UUID, List<SubscriptionBase>> subscriptionsFromAccountId = getSubscriptionsFromAccountId(context);
 
+        final List<SubscriptionBaseEvent> eventsForAccount = getEventsForAccountId(context);
+
         final Map<UUID, List<SubscriptionBase>> result = new HashMap<UUID, List<SubscriptionBase>>();
         for (final UUID bundleId : subscriptionsFromAccountId.keySet()) {
-            result.put(bundleId, buildBundleSubscriptions(bundleId, subscriptionsFromAccountId.get(bundleId), context));
+
+            final List<SubscriptionBase> subscriptionsForBundle = subscriptionsFromAccountId.get(bundleId);
+            final Collection<UUID> subscriptionIdsForBundle = Collections2.transform(subscriptionsForBundle, new Function<SubscriptionBase, UUID>() {
+                @Override
+                public UUID apply(final SubscriptionBase input) {
+                    return input.getId();
+                }
+            });
+            final Multimap<UUID, SubscriptionBaseEvent> eventsForSubscriptions = ArrayListMultimap.create();
+
+            for (final SubscriptionBase cur : subscriptionsForBundle) {
+                final Collection<SubscriptionBaseEvent> events= Collections2.filter(eventsForAccount, new Predicate<SubscriptionBaseEvent>() {
+                    @Override
+                    public boolean apply(final SubscriptionBaseEvent input) {
+                        return input.getSubscriptionId().equals(cur.getId());
+
+                    }
+                });
+                eventsForSubscriptions.putAll(cur.getId(), ImmutableList.copyOf(events));
+            }
+
+            result.put(bundleId, buildBundleSubscriptions(subscriptionsForBundle, eventsForSubscriptions,context));
         }
         return result;
     }
@@ -292,23 +318,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         return result;
     }
 
-    /*
-    @Override
-    public List<SubscriptionBase> getSubscriptionsForAccountAndKey(final UUID accountId,
-                                                               final String bundleKey, final InternalTenantContext callcontext) {
-        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<SubscriptionBase>>() {
-            @Override
-            public List<SubscriptionBase> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                final SubscriptionBundleModelDao bundleModel = entitySqlDaoWrapperFactory.become(BundleSqlDao.class).getBundlesFromAccountAndKey(accountId.toString(), bundleKey, callcontext);
-                if (bundleModel == null) {
-                    return Collections.emptyList();
-                }
-                return getSubscriptions(bundleModel.getId(), callcontext);
-            }
-        });
-    }
-    */
-
     @Override
     public void updateChargedThroughDate(final DefaultSubscriptionBase subscription, final InternalCallContext context) {
         final Date ctd = (subscription.getChargedThroughDate() != null) ? subscription.getChargedThroughDate().toDate() : null;
@@ -365,23 +374,12 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
             @Override
             public List<SubscriptionBaseEvent> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
                 final List<SubscriptionEventModelDao> models = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class).getEventsForSubscription(subscriptionId.toString(), context);
-                // Remove UNCANCEL events early on as they are not representative of a state transition but are just markers
-                final Collection<SubscriptionEventModelDao> filteredModels = Collections2.filter(models, new Predicate<SubscriptionEventModelDao>() {
-                    @Override
-                    public boolean apply(@Nullable final SubscriptionEventModelDao input) {
-                        return input.getUserType() != ApiEventType.UNCANCEL;
-                    }
-                });
-                return new ArrayList<SubscriptionBaseEvent>(Collections2.transform(filteredModels, new Function<SubscriptionEventModelDao, SubscriptionBaseEvent>() {
-                    @Override
-                    public SubscriptionBaseEvent apply(@Nullable final SubscriptionEventModelDao input) {
-                        return SubscriptionEventModelDao.toSubscriptionEvent(input);
-                    }
-                }));
+                return filterSubscriptionBaseEvents(models);
             }
         });
     }
 
+
     @Override
     public Map<UUID, List<SubscriptionBaseEvent>> getEventsForBundle(final UUID bundleId, final InternalTenantContext context) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Map<UUID, List<SubscriptionBaseEvent>>>() {
@@ -455,7 +453,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         });
     }
 
-
     @Override
     public void recreateSubscription(final DefaultSubscriptionBase subscription, final List<SubscriptionBaseEvent> recreateEvents, final InternalCallContext context) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
@@ -494,7 +491,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         });
     }
 
-
     @Override
     public void cancelSubscription(final DefaultSubscriptionBase subscription, final SubscriptionBaseEvent cancelEvent, final InternalCallContext context, final int seqId) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
@@ -557,9 +553,9 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
                 final UUID subscriptionId = subscription.getId();
 
                 final List<SubscriptionBaseEvent> changeEventsTweakedWithMigrateBilling = reinsertFutureMigrateBillingEventOnChangeFromTransaction(subscriptionId,
-                                                                                                                                              changeEvents,
-                                                                                                                                              entitySqlDaoWrapperFactory,
-                                                                                                                                              context);
+                                                                                                                                                   changeEvents,
+                                                                                                                                                   entitySqlDaoWrapperFactory,
+                                                                                                                                                   context);
 
                 cancelFutureEventsFromTransaction(subscriptionId, entitySqlDaoWrapperFactory, context);
 
@@ -669,6 +665,31 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         return changeEvents;
     }
 
+    private List<SubscriptionBaseEvent> filterSubscriptionBaseEvents(final List<SubscriptionEventModelDao> models) {
+        final Collection<SubscriptionEventModelDao> filteredModels = Collections2.filter(models, new Predicate<SubscriptionEventModelDao>() {
+            @Override
+            public boolean apply(@Nullable final SubscriptionEventModelDao input) {
+                return input.getUserType() != ApiEventType.UNCANCEL;
+            }
+        });
+        return new ArrayList<SubscriptionBaseEvent>(Collections2.transform(filteredModels, new Function<SubscriptionEventModelDao, SubscriptionBaseEvent>() {
+            @Override
+            public SubscriptionBaseEvent apply(@Nullable final SubscriptionEventModelDao input) {
+                return SubscriptionEventModelDao.toSubscriptionEvent(input);
+            }
+        }));
+    }
+
+    private List<SubscriptionBaseEvent> getEventsForAccountId(final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<SubscriptionBaseEvent>>() {
+            @Override
+            public List<SubscriptionBaseEvent> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final List<SubscriptionEventModelDao> models = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class).getByAccountRecordId(context);
+                return filterSubscriptionBaseEvents(models);
+            }
+        });
+    }
+
     private void cancelSubscriptionFromTransaction(final DefaultSubscriptionBase subscription, final SubscriptionBaseEvent cancelEvent, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context, final int seqId)
             throws EntityPersistenceException {
         final UUID subscriptionId = subscription.getId();
@@ -701,7 +722,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
     }
 
     private SubscriptionEventModelDao findFutureEventFromTransaction(final UUID subscriptionId, final EntitySqlDaoWrapperFactory<EntitySqlDao> dao, final EventType type,
-                                                                    @Nullable final ApiEventType apiType, final InternalCallContext context) {
+                                                                     @Nullable final ApiEventType apiType, final InternalCallContext context) {
 
         SubscriptionEventModelDao futureEvent = null;
         final Date now = clock.getUTCNow().toDate();
@@ -711,7 +732,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
                 (apiType == null || apiType == cur.getUserType())) {
                 if (futureEvent != null) {
                     throw new SubscriptionBaseError(String.format("Found multiple future events for type %s for subscriptions %s",
-                                                             type, subscriptionId.toString()));
+                                                                  type, subscriptionId.toString()));
                 }
                 futureEvent = cur;
                 // To check that there is only one such event
@@ -746,7 +767,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
             bundleInput.add(input);
         }
 
-        final List<SubscriptionBase> reloadedSubscriptions = buildBundleSubscriptions(input.getBundleId(), bundleInput, context);
+        final List<SubscriptionBase> reloadedSubscriptions = buildBundleSubscriptions(bundleInput, null, context);
         for (final SubscriptionBase cur : reloadedSubscriptions) {
             if (cur.getId().equals(input.getId())) {
                 return cur;
@@ -756,7 +777,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         throw new SubscriptionBaseError("Unexpected code path in buildSubscription");
     }
 
-    private List<SubscriptionBase> buildBundleSubscriptions(final UUID bundleId, final List<SubscriptionBase> input, final InternalTenantContext context) {
+    private List<SubscriptionBase> buildBundleSubscriptions(final List<SubscriptionBase> input, @Nullable final Multimap<UUID, SubscriptionBaseEvent> eventsForSubscription, final InternalTenantContext context) {
         if (input == null || input.size() == 0) {
             return Collections.emptyList();
         }
@@ -778,7 +799,12 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         SubscriptionBaseEvent futureBaseEvent = null;
         final List<SubscriptionBase> result = new ArrayList<SubscriptionBase>(input.size());
         for (final SubscriptionBase cur : input) {
-            final List<SubscriptionBaseEvent> events = getEventsForSubscription(cur.getId(), context);
+
+
+            final List<SubscriptionBaseEvent> events = eventsForSubscription != null ?
+                                                       (List<SubscriptionBaseEvent>) eventsForSubscription.get(cur.getId()) :
+                                                       getEventsForSubscription(cur.getId(), context);
+
             SubscriptionBase reloaded = createSubscriptionForInternalUse(cur, events);
 
             switch (cur.getCategory()) {
@@ -806,15 +832,15 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
                     if (createCancelEvent && reloaded.getFutureEndDate() == null) {
                         final DateTime now = clock.getUTCNow();
                         final SubscriptionBaseEvent addOnCancelEvent = new ApiEventCancel(new ApiEventBuilder()
-                                                                                             .setSubscriptionId(reloaded.getId())
-                                                                                             .setActiveVersion(((DefaultSubscriptionBase) reloaded).getActiveVersion())
-                                                                                             .setProcessedDate(now)
-                                                                                             .setEffectiveDate(futureBaseEvent.getEffectiveDate())
-                                                                                             .setRequestedDate(now)
-                                                                                             .setCreatedDate(futureBaseEvent.getCreatedDate())
-                                                                                                     // This event is only there to indicate the ADD_ON is future canceled, but it is not there
-                                                                                                     // on disk until the base plan cancellation becomes effective
-                                                                                             .setFromDisk(false));
+                                                                                                  .setSubscriptionId(reloaded.getId())
+                                                                                                  .setActiveVersion(((DefaultSubscriptionBase) reloaded).getActiveVersion())
+                                                                                                  .setProcessedDate(now)
+                                                                                                  .setEffectiveDate(futureBaseEvent.getEffectiveDate())
+                                                                                                  .setRequestedDate(now)
+                                                                                                  .setCreatedDate(futureBaseEvent.getCreatedDate())
+                                                                                                          // This event is only there to indicate the ADD_ON is future canceled, but it is not there
+                                                                                                          // on disk until the base plan cancellation becomes effective
+                                                                                                  .setFromDisk(false));
 
                         events.add(addOnCancelEvent);
                         // Finally reload subscription with full set of events
@@ -873,7 +899,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
                 try {
                     // Note: we don't send a requested change event here, but a repair event
                     final RepairSubscriptionInternalEvent busEvent = new DefaultRepairSubscriptionEvent(accountId, bundleId, clock.getUTCNow(),
-                                                                                                      context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
+                                                                                                        context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
                     eventBus.postFromTransaction(busEvent, entitySqlDaoWrapperFactory.getSqlDao());
                 } catch (EventBusException e) {
                     log.warn("Failed to post repair subscription event for bundle " + bundleId, e);
@@ -922,7 +948,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         return null;
     }
 
-
     //
     // Either records a notfication or sends a bus event is operation is immediate
     //
@@ -953,7 +978,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
                                                                                                       context.getUserToken(),
                                                                                                       context.getAccountRecordId(), context.getTenantRecordId());
 
-
             eventBus.postFromTransaction(busEvent, entitySqlDaoWrapperFactory.getSqlDao());
         } catch (EventBusException e) {
             log.warn("Failed to post effective event for subscription " + subscription.getId(), e);
diff --git a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionBundleModelDao.java b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionBundleModelDao.java
index 4cbabcc..9af84a0 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionBundleModelDao.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionBundleModelDao.java
@@ -64,6 +64,22 @@ public class SubscriptionBundleModelDao extends EntityBase implements EntityMode
         return originalCreatedDate;
     }
 
+    public void setExternalKey(final String externalKey) {
+        this.externalKey = externalKey;
+    }
+
+    public void setAccountId(final UUID accountId) {
+        this.accountId = accountId;
+    }
+
+    public void setLastSysUpdateDate(final DateTime lastSysUpdateDate) {
+        this.lastSysUpdateDate = lastSysUpdateDate;
+    }
+
+    public void setOriginalCreatedDate(final DateTime originalCreatedDate) {
+        this.originalCreatedDate = originalCreatedDate;
+    }
+
     public static SubscriptionBaseBundle toSubscriptionbundle(final SubscriptionBundleModelDao src) {
         if (src == null) {
             return null;
diff --git a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java
index b4d94e4..8a53635 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java
@@ -142,6 +142,50 @@ public class SubscriptionEventModelDao extends EntityBase implements EntityModel
         return isActive;
     }
 
+    public void setTotalOrdering(final long totalOrdering) {
+        this.totalOrdering = totalOrdering;
+    }
+
+    public void setEventType(final EventType eventType) {
+        this.eventType = eventType;
+    }
+
+    public void setUserType(final ApiEventType userType) {
+        this.userType = userType;
+    }
+
+    public void setRequestedDate(final DateTime requestedDate) {
+        this.requestedDate = requestedDate;
+    }
+
+    public void setEffectiveDate(final DateTime effectiveDate) {
+        this.effectiveDate = effectiveDate;
+    }
+
+    public void setSubscriptionId(final UUID subscriptionId) {
+        this.subscriptionId = subscriptionId;
+    }
+
+    public void setPlanName(final String planName) {
+        this.planName = planName;
+    }
+
+    public void setPhaseName(final String phaseName) {
+        this.phaseName = phaseName;
+    }
+
+    public void setPriceListName(final String priceListName) {
+        this.priceListName = priceListName;
+    }
+
+    public void setCurrentVersion(final long currentVersion) {
+        this.currentVersion = currentVersion;
+    }
+
+    public void setIsActive(final boolean isActive) {
+        this.isActive = isActive;
+    }
+
     public static SubscriptionBaseEvent toSubscriptionEvent(final SubscriptionEventModelDao src) {
 
         if (src == null) {
diff --git a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionModelDao.java b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionModelDao.java
index f650cb5..bd181c6 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionModelDao.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/model/SubscriptionModelDao.java
@@ -85,6 +85,34 @@ public class SubscriptionModelDao extends EntityBase implements EntityModelDao<S
         return paidThroughDate;
     }
 
+    public void setBundleId(final UUID bundleId) {
+        this.bundleId = bundleId;
+    }
+
+    public void setCategory(final ProductCategory category) {
+        this.category = category;
+    }
+
+    public void setStartDate(final DateTime startDate) {
+        this.startDate = startDate;
+    }
+
+    public void setBundleStartDate(final DateTime bundleStartDate) {
+        this.bundleStartDate = bundleStartDate;
+    }
+
+    public void setActiveVersion(final long activeVersion) {
+        this.activeVersion = activeVersion;
+    }
+
+    public void setChargedThroughDate(final DateTime chargedThroughDate) {
+        this.chargedThroughDate = chargedThroughDate;
+    }
+
+    public void setPaidThroughDate(final DateTime paidThroughDate) {
+        this.paidThroughDate = paidThroughDate;
+    }
+
     public static SubscriptionBase toSubscription(final SubscriptionModelDao src) {
         if (src == null) {
             return null;
diff --git a/subscription/src/main/java/com/ning/billing/subscription/glue/DefaultSubscriptionModule.java b/subscription/src/main/java/com/ning/billing/subscription/glue/DefaultSubscriptionModule.java
index 0c35d1c..953a2ea 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/glue/DefaultSubscriptionModule.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/glue/DefaultSubscriptionModule.java
@@ -23,6 +23,7 @@ import com.ning.billing.glue.SubscriptionModule;
 import com.ning.billing.subscription.alignment.MigrationPlanAligner;
 import com.ning.billing.subscription.alignment.PlanAligner;
 import com.ning.billing.subscription.api.SubscriptionBaseApiService;
+import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 import com.ning.billing.subscription.api.SubscriptionBaseService;
 import com.ning.billing.subscription.api.migration.DefaultSubscriptionBaseMigrationApi;
 import com.ning.billing.subscription.api.migration.SubscriptionBaseMigrationApi;
@@ -40,7 +41,6 @@ import com.ning.billing.subscription.engine.dao.DefaultSubscriptionDao;
 import com.ning.billing.subscription.engine.dao.RepairSubscriptionDao;
 import com.ning.billing.subscription.engine.dao.SubscriptionDao;
 import com.ning.billing.util.config.SubscriptionConfig;
-import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.name.Names;
@@ -68,7 +68,6 @@ public class DefaultSubscriptionModule extends AbstractModule implements Subscri
     }
 
     protected void installSubscriptionCore() {
-
         bind(SubscriptionBaseApiService.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairSubscriptionApiService.class).asEagerSingleton();
         bind(SubscriptionBaseApiService.class).to(DefaultSubscriptionBaseApiService.class).asEagerSingleton();
 
@@ -106,7 +105,6 @@ public class DefaultSubscriptionModule extends AbstractModule implements Subscri
         bind(SubscriptionBaseMigrationApi.class).to(DefaultSubscriptionBaseMigrationApi.class).asEagerSingleton();
     }
 
-
     @Override
     public void installSubscriptionInternalApi() {
         bind(SubscriptionBaseInternalApi.class).to(DefaultSubscriptionInternalApi.class).asEagerSingleton();
diff --git a/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/BundleSqlDao.sql.stg b/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/BundleSqlDao.sql.stg
index 390af6f..b026267 100644
--- a/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/BundleSqlDao.sql.stg
+++ b/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/BundleSqlDao.sql.stg
@@ -42,7 +42,7 @@ from bundles
 where
 external_key = :externalKey
 <AND_CHECK_TENANT()>
-order by record_id asc
+<defaultOrderBy()>
 ;
 >>
 
@@ -53,7 +53,7 @@ where
 external_key = :externalKey
 and account_id = :accountId
 <AND_CHECK_TENANT()>
-order by record_id asc
+<defaultOrderBy()>
 ;
 >>
 
@@ -63,6 +63,6 @@ from bundles
 where
 account_id = :accountId
 <AND_CHECK_TENANT()>
-order by record_id asc
+<defaultOrderBy()>
 ;
 >>
diff --git a/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg b/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg
index 3008da9..4550610 100644
--- a/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg
+++ b/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg
@@ -8,6 +8,11 @@ extraTableFieldsWithComma(prefix) ::= <<
 , <prefix>record_id as total_ordering
 >>
 
+defaultOrderBy(prefix) ::= <<
+order by <prefix>effective_date ASC, <recordIdField(prefix)> ASC
+>>
+
+
 tableFields(prefix) ::= <<
   <prefix> event_type
 , <prefix> user_type
@@ -90,9 +95,7 @@ subscription_id = :subscriptionId
 and is_active = 1
 and effective_date > :now
 <AND_CHECK_TENANT()>
-order by
-effective_date asc
-, record_id asc
+<defaultOrderBy()>
 ;
 >> 
 
@@ -104,9 +107,7 @@ where
 subscription_id = :subscriptionId
 and is_active = 1
 <AND_CHECK_TENANT()>
-order by
-effective_date asc
-, record_id asc
+<defaultOrderBy()>
 ;
 >>
 
diff --git a/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg b/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg
index 378afef..ad122a2 100644
--- a/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg
+++ b/subscription/src/main/resources/com/ning/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg
@@ -37,6 +37,7 @@ select
 from <tableName()>
 where bundle_id = :bundleId
 <AND_CHECK_TENANT()>
+<defaultOrderBy()>
 ;
 >>
 

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

diff --git a/tenant/pom.xml b/tenant/pom.xml
index afc66bf..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.3-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-tenant</artifactId>
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVModelDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVModelDao.java
index cebbd8f..0e281c0 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVModelDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVModelDao.java
@@ -53,6 +53,18 @@ public class TenantKVModelDao extends EntityBase implements EntityModelDao<Tenan
         return isActive;
     }
 
+    public void setTenantKey(final String tenantKey) {
+        this.tenantKey = tenantKey;
+    }
+
+    public void setTenantValue(final String tenantValue) {
+        this.tenantValue = tenantValue;
+    }
+
+    public void setIsActive(final Boolean isActive) {
+        this.isActive = isActive;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantModelDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantModelDao.java
index c310649..e6bc707 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantModelDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantModelDao.java
@@ -64,6 +64,22 @@ public class TenantModelDao extends EntityBase implements EntityModelDao<Tenant>
         return apiSalt;
     }
 
+    public void setExternalKey(final String externalKey) {
+        this.externalKey = externalKey;
+    }
+
+    public void setApiKey(final String apiKey) {
+        this.apiKey = apiKey;
+    }
+
+    public void setApiSecret(final String apiSecret) {
+        this.apiSecret = apiSecret;
+    }
+
+    public void setApiSalt(final String apiSalt) {
+        this.apiSalt = apiSalt;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();

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

diff --git a/usage/pom.xml b/usage/pom.xml
index f2d5cec..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.3-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-usage</artifactId>
diff --git a/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageModelDao.java b/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageModelDao.java
index 34a45f3..2ef4ea3 100644
--- a/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageModelDao.java
+++ b/usage/src/main/java/com/ning/billing/usage/dao/RolledUpUsageModelDao.java
@@ -66,6 +66,30 @@ public class RolledUpUsageModelDao {
         return amount;
     }
 
+    public void setId(final UUID id) {
+        this.id = id;
+    }
+
+    public void setSubscriptionId(final UUID subscriptionId) {
+        this.subscriptionId = subscriptionId;
+    }
+
+    public void setUnitType(final String unitType) {
+        this.unitType = unitType;
+    }
+
+    public void setStartTime(final DateTime startTime) {
+        this.startTime = startTime;
+    }
+
+    public void setEndTime(final DateTime endTime) {
+        this.endTime = endTime;
+    }
+
+    public void setAmount(final BigDecimal amount) {
+        this.amount = amount;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();

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

diff --git a/util/pom.xml b/util/pom.xml
index 01c5fba..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.3-SNAPSHOT</version>
+        <version>0.8.6-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldModelDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldModelDao.java
index fdeab59..e3d4b1e 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldModelDao.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldModelDao.java
@@ -65,6 +65,22 @@ public class CustomFieldModelDao extends EntityBase implements EntityModelDao<Cu
         return objectType;
     }
 
+    public void setFieldName(final String fieldName) {
+        this.fieldName = fieldName;
+    }
+
+    public void setFieldValue(final String fieldValue) {
+        this.fieldValue = fieldValue;
+    }
+
+    public void setObjectId(final UUID objectId) {
+        this.objectId = objectId;
+    }
+
+    public void setObjectType(final ObjectType objectType) {
+        this.objectType = objectType;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/util/src/main/java/com/ning/billing/util/customfield/ShouldntHappenException.java b/util/src/main/java/com/ning/billing/util/customfield/ShouldntHappenException.java
new file mode 100644
index 0000000..5d4cc2d
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/ShouldntHappenException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.customfield;
+
+public class ShouldntHappenException extends RuntimeException {
+
+    public ShouldntHappenException(final String message) {
+        super(message);
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/dao/EntityHistoryModelDao.java b/util/src/main/java/com/ning/billing/util/dao/EntityHistoryModelDao.java
index 791f89d..28ce9fa 100644
--- a/util/src/main/java/com/ning/billing/util/dao/EntityHistoryModelDao.java
+++ b/util/src/main/java/com/ning/billing/util/dao/EntityHistoryModelDao.java
@@ -27,9 +27,9 @@ import com.ning.billing.util.entity.dao.EntityModelDao;
 
 public class EntityHistoryModelDao<M extends EntityModelDao<E>, E extends Entity> extends EntityBase {
 
-    private final Long targetRecordId;
-    private final M entity;
-    private final ChangeType changeType;
+    private Long targetRecordId;
+    private M entity;
+    private ChangeType changeType;
 
     public EntityHistoryModelDao(final UUID id, final M src, final Long targetRecordId, final ChangeType type, final DateTime createdDate) {
         super(id, createdDate, createdDate);
@@ -53,4 +53,16 @@ public class EntityHistoryModelDao<M extends EntityModelDao<E>, E extends Entity
     public Long getTargetRecordId() {
         return targetRecordId;
     }
+
+    public void setTargetRecordId(final Long targetRecordId) {
+        this.targetRecordId = targetRecordId;
+    }
+
+    public void setEntity(final M entity) {
+        this.entity = entity;
+    }
+
+    public void setChangeType(final ChangeType changeType) {
+        this.changeType = changeType;
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionModelDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionModelDao.java
index d8db1b0..5de4cfd 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionModelDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionModelDao.java
@@ -66,6 +66,18 @@ public class TagDefinitionModelDao extends EntityBase implements EntityModelDao<
         return isActive;
     }
 
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public void setDescription(final String description) {
+        this.description = description;
+    }
+
+    public void setIsActive(final Boolean isActive) {
+        this.isActive = isActive;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagModelDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagModelDao.java
index bb9b609..5cfe1ea 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagModelDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagModelDao.java
@@ -69,6 +69,22 @@ public class TagModelDao extends EntityBase implements EntityModelDao<Tag> {
         return isActive;
     }
 
+    public void setTagDefinitionId(final UUID tagDefinitionId) {
+        this.tagDefinitionId = tagDefinitionId;
+    }
+
+    public void setObjectId(final UUID objectId) {
+        this.objectId = objectId;
+    }
+
+    public void setObjectType(final ObjectType objectType) {
+        this.objectType = objectType;
+    }
+
+    public void setIsActive(final Boolean isActive) {
+        this.isActive = isActive;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/util/src/main/java/com/ning/billing/util/timezone/DateAndTimeZoneContext.java b/util/src/main/java/com/ning/billing/util/timezone/DateAndTimeZoneContext.java
index b9e60b1..c96a9f1 100644
--- a/util/src/main/java/com/ning/billing/util/timezone/DateAndTimeZoneContext.java
+++ b/util/src/main/java/com/ning/billing/util/timezone/DateAndTimeZoneContext.java
@@ -18,6 +18,7 @@ package com.ning.billing.util.timezone;
 
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
+import org.joda.time.Days;
 import org.joda.time.LocalDate;
 import org.joda.time.LocalTime;
 
@@ -31,6 +32,7 @@ import com.ning.billing.clock.Clock;
 public final class DateAndTimeZoneContext {
 
     private final LocalTime referenceTime;
+    private final int offsetFromUtc;
     private final DateTimeZone accountTimeZone;
     private final Clock clock;
 
@@ -38,6 +40,13 @@ public final class DateAndTimeZoneContext {
         this.clock = clock;
         this.referenceTime = effectiveDateTime != null ? effectiveDateTime.toLocalTime() : null;
         this.accountTimeZone = accountTimeZone;
+        this.offsetFromUtc = computeOffsetFromUtc(effectiveDateTime, accountTimeZone);
+    }
+
+    static int computeOffsetFromUtc(final DateTime effectiveDateTime, final DateTimeZone accountTimeZone) {
+        final LocalDate localDateInAccountTimeZone = new LocalDate(effectiveDateTime, accountTimeZone);
+        final LocalDate localDateInUTC = new LocalDate(effectiveDateTime, DateTimeZone.UTC);
+        return Days.daysBetween(localDateInUTC, localDateInAccountTimeZone).getDays();
     }
 
     public LocalDate computeTargetDate(final DateTime targetDateTime) {
@@ -50,9 +59,9 @@ public final class DateAndTimeZoneContext {
         // Since we create the targetDate for next invoice using the date from the notificationQ, we need to make sure
         // that this datetime once transformed into a LocalDate points to the correct day.
         //
-        // e.g If accountTimeZone is -8 and we want to invoice on the 16, with a toDateTimeAtCurrentTime = 00:00:23,
-        // we will generate a datetime that is 16T08:00:23 => LocalDate in that timeZone stays on the 16.
-        //
+        // All we need to do is figure is the transformation from DateTime (point in time) to LocalDate (date in account time zone)
+        // changed the day; if so, when we recompute a UTC date from LocalDate (date in account time zone), we can simply chose a reference
+        // time and apply the offset backward to end up on the right day
         //
         // We use clock.getUTCNow() to get the offset with account timezone but that may not be correct
         // when we transition from standard time and daylight saving time. We could end up with a result
@@ -60,9 +69,7 @@ public final class DateAndTimeZoneContext {
         // We will fix that by re-inserting ourselves in the notificationQ if we detect that there is no invoice
         // and yet the subscription is recurring and not cancelled.
         //
-        final int utcOffest = accountTimeZone.getOffset(clock.getUTCNow());
-        final int localToUTCOffest = -1 * utcOffest;
-        return invoiceItemEndDate.toDateTime(referenceTime, DateTimeZone.UTC).plusMillis(localToUTCOffest);
+        return invoiceItemEndDate.toDateTime(referenceTime, DateTimeZone.UTC).plusDays(-offsetFromUtc);
     }
 
     public DateTime computeUTCDateTimeFromNow() {
diff --git a/util/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestBase.java b/util/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestBase.java
index 84a5e3a..7d6c11c 100644
--- a/util/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestBase.java
+++ b/util/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestBase.java
@@ -29,6 +29,7 @@ import com.ning.billing.events.InvoiceCreationInternalEvent;
 import com.ning.billing.events.NullInvoiceInternalEvent;
 import com.ning.billing.events.PaymentErrorInternalEvent;
 import com.ning.billing.events.PaymentInfoInternalEvent;
+import com.ning.billing.events.PaymentPluginErrorInternalEvent;
 
 public class CompletionUserRequestBase implements CompletionUserRequest {
 
@@ -123,6 +124,9 @@ public class CompletionUserRequestBase implements CompletionUserRequest {
             case PAYMENT_ERROR:
                 onPaymentError((PaymentErrorInternalEvent) curEvent);
                 break;
+            case PAYMENT_PLUGIN_ERROR:
+                onPaymentPluginError((PaymentPluginErrorInternalEvent) curEvent);
+                break;
             default:
                 throw new RuntimeException("Unexpected event type " + curEvent.getBusEventType());
         }
@@ -157,4 +161,7 @@ public class CompletionUserRequestBase implements CompletionUserRequest {
     public void onPaymentError(final PaymentErrorInternalEvent curEvent) {
     }
 
+    @Override
+    public void onPaymentPluginError(final PaymentPluginErrorInternalEvent curEvent) {
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestWaiter.java b/util/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestWaiter.java
index 99b34dc..898ad3e 100644
--- a/util/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestWaiter.java
+++ b/util/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestWaiter.java
@@ -27,6 +27,7 @@ import com.ning.billing.events.InvoiceCreationInternalEvent;
 import com.ning.billing.events.NullInvoiceInternalEvent;
 import com.ning.billing.events.PaymentErrorInternalEvent;
 import com.ning.billing.events.PaymentInfoInternalEvent;
+import com.ning.billing.events.PaymentPluginErrorInternalEvent;
 
 public interface CompletionUserRequestWaiter {
 
@@ -45,4 +46,6 @@ public interface CompletionUserRequestWaiter {
     public void onPaymentInfo(final PaymentInfoInternalEvent curEvent);
 
     public void onPaymentError(final PaymentErrorInternalEvent curEvent);
+
+    public void onPaymentPluginError(final PaymentPluginErrorInternalEvent curEvent);
 }
diff --git a/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.sql.stg
index 2f8b857..1a91d2f 100644
--- a/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.sql.stg
@@ -35,6 +35,7 @@ where
 object_id = :objectId
 and object_type = :objectType
 <AND_CHECK_TENANT()>
+<defaultOrderBy()>
 ;
 >>
 
diff --git a/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
index 297a502..64a66fd 100644
--- a/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
@@ -31,6 +31,10 @@ andCheckSoftDeletionWithComma(prefix) ::= ""
 /** Add extra fields for SELECT queries **/
 extraTableFieldsWithComma(prefix) ::= ""
 
+defaultOrderBy(prefix) ::= <<
+order by <recordIdField(prefix)> ASC
+>>
+
 /******************   To override in each EntitySqlDao template file <end>  *****************************/
 
 
@@ -143,6 +147,7 @@ select
 from <tableName()> t
 where <CHECK_TENANT("t.")>
 <andCheckSoftDeletionWithComma("t.")>
+<defaultOrderBy("t.")>
 ;
 >>
 
@@ -194,6 +199,7 @@ from <tableName()> t
 where <accountRecordIdField("t.")> \<=\> :accountRecordId
 <andCheckSoftDeletionWithComma("t.")>
 <AND_CHECK_TENANT("t.")>
+<defaultOrderBy("t.")>
 ;
 >>
 
@@ -334,7 +340,7 @@ where t.target_record_id = :targetRecordId
 and t.table_name = :tableName
 <andCheckSoftDeletionWithComma("t.")>
 <AND_CHECK_TENANT("t.")>
-order by <recordIdField("t.")> ASC
+<defaultOrderBy("t.")>
 ;
 >>
 
@@ -352,7 +358,7 @@ join (
 ) history_record_ids on t.target_record_id = history_record_ids.record_id
 where t.table_name = :tableName
 <AND_CHECK_TENANT("t.")>
-order by <recordIdField("t.")> ASC
+<defaultOrderBy("t.")>
 ;
 >>
 
diff --git a/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java b/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
index 407e98f..60f9fe8 100644
--- a/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
+++ b/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
@@ -18,6 +18,7 @@ package com.ning.billing.mock.glue;
 
 import org.mockito.Mockito;
 
+import com.ning.billing.entitlement.EntitlementInternalApi;
 import com.ning.billing.entitlement.api.EntitlementApi;
 import com.ning.billing.entitlement.api.SubscriptionApi;
 import com.ning.billing.glue.EntitlementModule;
@@ -29,6 +30,7 @@ public class MockEntitlementModule extends AbstractModule implements Entitlement
 
     private final BlockingInternalApi blockingApi = Mockito.mock(BlockingInternalApi.class);
     private final EntitlementApi entitlementApi = Mockito.mock(EntitlementApi.class);
+    private final EntitlementInternalApi entitlementInternalApi = Mockito.mock(EntitlementInternalApi.class);
     private final SubscriptionApi subscriptionApi = Mockito.mock(SubscriptionApi.class);
 
     @Override
@@ -54,6 +56,11 @@ public class MockEntitlementModule extends AbstractModule implements Entitlement
     }
 
     @Override
+    public void installEntitlementInternalApi() {
+        bind(EntitlementInternalApi.class).toInstance(entitlementInternalApi);
+    }
+
+    @Override
     public void installSubscriptionApi() {
         bind(SubscriptionApi.class).toInstance(subscriptionApi);
     }
@@ -61,5 +68,4 @@ public class MockEntitlementModule extends AbstractModule implements Entitlement
     @Override
     public void installBlockingChecker() {
     }
-
 }
diff --git a/util/src/test/java/com/ning/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java b/util/src/test/java/com/ning/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
index 6fe8609..7d11d99 100644
--- a/util/src/test/java/com/ning/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
+++ b/util/src/test/java/com/ning/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
@@ -27,6 +27,7 @@ import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.ning.billing.ObjectType;
+import com.ning.billing.api.TestApiListener.NextEvent;
 import com.ning.billing.util.UtilTestSuiteWithEmbeddedDB;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.customfield.StringCustomField;
@@ -35,7 +36,6 @@ import com.google.common.collect.ImmutableList;
 
 public class TestDefaultCustomFieldUserApi extends UtilTestSuiteWithEmbeddedDB {
 
-
     @Test(groups = "slow")
     public void testSaveCustomFieldWithAccountRecordId() throws Exception {
         final UUID accountId = UUID.randomUUID();
@@ -53,7 +53,9 @@ public class TestDefaultCustomFieldUserApi extends UtilTestSuiteWithEmbeddedDB {
         });
 
         final CustomField customField = new StringCustomField(UUID.randomUUID().toString().substring(1, 4), UUID.randomUUID().toString().substring(1, 4), ObjectType.ACCOUNT, accountId, callContext.getCreatedDate());
+        eventsListener.pushExpectedEvent(NextEvent.CUSTOM_FIELD);
         customFieldUserApi.addCustomFields(ImmutableList.<CustomField>of(customField), callContext);
+        assertListenerStatus();
 
         // Verify the field was saved
         final List<CustomField> customFields = customFieldUserApi.getCustomFieldsForObject(accountId, ObjectType.ACCOUNT, callContext);
diff --git a/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java b/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java
index 1306cfe..df148e1 100644
--- a/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java
+++ b/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java
@@ -112,6 +112,7 @@ public class TestStringTemplateInheritance extends UtilTestSuiteNoDB {
                                                                          ", t.tenant_record_id\n" +
                                                                          "from kombucha t\n" +
                                                                          "where t.tenant_record_id = :tenantRecordId\n" +
+                                                                         "order by t.record_id ASC\n" +
                                                                          ";");
         Assert.assertEquals(kombucha.getInstanceOf("get", ImmutableMap.<String, String>of("orderBy", "recordId", "offset", "3", "rowCount", "12")).toString(), "select SQL_CALC_FOUND_ROWS\n" +
                                                                                                                                                                "  t.record_id\n" +
diff --git a/util/src/test/java/com/ning/billing/util/timezone/TestDateAndTimeZoneContext.java b/util/src/test/java/com/ning/billing/util/timezone/TestDateAndTimeZoneContext.java
new file mode 100644
index 0000000..f73a120
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/timezone/TestDateAndTimeZoneContext.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.timezone;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+import org.testng.annotations.Test;
+
+import com.ning.billing.util.UtilTestSuiteNoDB;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+//
+// There are two categories of tests, one that test the offset calculation and one that calculates
+// how to get a DateTime from a LocalDate (in account time zone)
+//
+// Tests {1, 2, 3} use an account timezone with a negative offset (-8) and tests {A, B, C} use an account timezone with a positive offset (+8)
+//
+public class TestDateAndTimeZoneContext extends UtilTestSuiteNoDB {
+
+    private final DateTimeFormatter DATE_TIME_FORMATTER = ISODateTimeFormat.dateTimeParser();
+
+    final String effectiveDateTime1 = "2012-01-20T07:30:42.000Z";
+    final String effectiveDateTime2 = "2012-01-20T08:00:00.000Z";
+    final String effectiveDateTime3 = "2012-01-20T08:45:33.000Z";
+
+    final String effectiveDateTimeA = "2012-01-20T16:30:42.000Z";
+    final String effectiveDateTimeB = "2012-01-20T16:00:00.000Z";
+    final String effectiveDateTimeC = "2012-01-20T15:30:42.000Z";
+
+
+    //
+    // Take an negative timezone offset and a reference time that is less than the offset (07:30:42 < 8)
+    // => to expect a negative offset of one day
+    //
+    @Test(groups = "fast")
+    public void testComputeOffset1() {
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime1);
+
+        int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+        assertEquals(offset, -1);
+    }
+
+    //
+    // Take an negative timezone offset and a reference time that is equal than the offset (08:00:00 = 8)
+    // => to expect an offset of 0
+    //
+    @Test(groups = "fast")
+    public void testComputeOffset2() {
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime2);
+
+        int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+        assertEquals(offset, 0);
+    }
+
+    //
+    // Take an negative timezone offset and a reference time that is greater than the offset (08:45:33 > 8)
+    // => to expect an offset of 0
+    //
+    @Test(groups = "fast")
+    public void testComputeOffset3() {
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime3);
+
+        int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+        assertEquals(offset, 0);
+    }
+
+    //
+    // Take an positive timezone offset and a reference time that closer to the end of the day than the timezone (16:30:42 + 8 > 24)
+    // => to expect a positive offset of one day
+    //
+    @Test(groups = "fast")
+    public void testComputeOffsetA() {
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeA);
+
+        int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+        assertEquals(offset, 1);
+    }
+
+    //
+    // Take an positive timezone offset and a reference time that brings us exactly at the end of the day (16:00:00 + 8 = 24)
+    // => to expect an offset of 1
+    //
+    @Test(groups = "fast")
+    public void testComputeOffsetB() {
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeB);
+
+        int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+        assertEquals(offset, 1);
+    }
+
+    //
+    // Take an positive timezone offset and a reference time that further away to the end of the day  (15:30:42 + 8 < 24)
+    // =>  to expect an offset of 0
+    //
+    @Test(groups = "fast")
+    public void testComputeOffsetC() {
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeC);
+
+        int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+        assertEquals(offset, 0);
+    }
+
+    @Test(groups = "fast")
+    public void testComputeUTCDateTimeFromLocalDate1() {
+
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime1);
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+        final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+
+        final LocalDate endDate = new LocalDate(2013, 01, 19);
+        final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
+        assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+    }
+
+
+    @Test(groups = "fast")
+    public void testComputeUTCDateTimeFromLocalDate2() {
+
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime2);
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+        final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+
+        final LocalDate endDate = new LocalDate(2013, 01, 20);
+        final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
+        assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+    }
+
+
+    @Test(groups = "fast")
+    public void testComputeUTCDateTimeFromLocalDate3() {
+
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime3);
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
+        final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+
+        final LocalDate endDate = new LocalDate(2013, 01, 20);
+        final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
+        assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+    }
+
+    @Test(groups = "fast")
+    public void testComputeUTCDateTimeFromLocalDateA() {
+
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeA);
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+        final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+
+        final LocalDate endDate = new LocalDate(2013, 01, 21);
+        final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
+        assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+    }
+
+    @Test(groups = "fast")
+    public void testComputeUTCDateTimeFromLocalDateB() {
+
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeB);
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+        final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+
+        final LocalDate endDate = new LocalDate(2013, 01, 21);
+        final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
+        assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+    }
+
+    @Test(groups = "fast")
+    public void testComputeUTCDateTimeFromLocalDateC() {
+
+        final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeC);
+
+        final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
+        final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+
+        final LocalDate endDate = new LocalDate(2013, 01, 20);
+        final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
+        assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
+    }
+}