killbill-memoizeit

analytics: more work on BST Signed-off-by: Pierre-Alexandre

4/3/2013 4:24:38 PM

Details

diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessSubscriptionTransition.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessSubscriptionTransition.java
index f9a0813..ddf25fd 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessSubscriptionTransition.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessSubscriptionTransition.java
@@ -23,13 +23,11 @@ import org.joda.time.DateTime;
 
 import com.ning.billing.osgi.bundles.analytics.model.BusinessSubscriptionTransitionModelDao;
 
-public class BusinessSubscriptionTransition {
+public class BusinessSubscriptionTransition extends BusinessEntityBase {
 
     private final long totalOrdering;
     private final UUID bundleId;
-    private final String externalKey;
-    private final UUID accountId;
-    private final String accountKey;
+    private final String bundleExternalKey;
     private final UUID subscriptionId;
 
     private final DateTime requestedTimestamp;
@@ -46,6 +44,7 @@ public class BusinessSubscriptionTransition {
     private final String prevPriceList;
     private final BigDecimal prevMrr;
     private final String prevCurrency;
+    private final Boolean prevBusinessActive;
     private final DateTime prevStartDate;
     private final String prevState;
 
@@ -59,15 +58,23 @@ public class BusinessSubscriptionTransition {
     private final String nextPriceList;
     private final BigDecimal nextMrr;
     private final String nextCurrency;
+    private final Boolean nextBusinessActive;
     private final DateTime nextStartDate;
+    private final DateTime nextEndDate;
     private final String nextState;
 
     public BusinessSubscriptionTransition(final BusinessSubscriptionTransitionModelDao bstModelDao) {
+        super(bstModelDao.getCreatedDate(),
+              bstModelDao.getCreatedBy(),
+              bstModelDao.getCreatedReasonCode(),
+              bstModelDao.getCreatedComments(),
+              bstModelDao.getAccountId(),
+              bstModelDao.getAccountName(),
+              bstModelDao.getAccountExternalKey());
+
         this.totalOrdering = bstModelDao.getTotalOrdering();
         this.bundleId = bstModelDao.getBundleId();
-        this.externalKey = bstModelDao.getExternalKey();
-        this.accountId = bstModelDao.getAccountId();
-        this.accountKey = bstModelDao.getAccountKey();
+        this.bundleExternalKey = bstModelDao.getBundleExternalKey();
         this.subscriptionId = bstModelDao.getSubscriptionId();
 
         this.requestedTimestamp = bstModelDao.getRequestedTimestamp();
@@ -89,6 +96,7 @@ public class BusinessSubscriptionTransition {
             this.prevPriceList = bstModelDao.getPreviousSubscription().getPriceList();
             this.prevMrr = bstModelDao.getPreviousSubscription().getMrr();
             this.prevCurrency = bstModelDao.getPreviousSubscription().getCurrency();
+            this.prevBusinessActive = bstModelDao.getPreviousSubscription().getBusinessActive();
             this.prevStartDate = bstModelDao.getPreviousSubscription().getStartDate();
             this.prevState = bstModelDao.getPreviousSubscription().getState().toString();
         } else {
@@ -102,6 +110,7 @@ public class BusinessSubscriptionTransition {
             this.prevPriceList = null;
             this.prevMrr = null;
             this.prevCurrency = null;
+            this.prevBusinessActive = null;
             this.prevStartDate = null;
             this.prevState = null;
         }
@@ -117,7 +126,9 @@ public class BusinessSubscriptionTransition {
             this.nextPriceList = bstModelDao.getNextSubscription().getPriceList();
             this.nextMrr = bstModelDao.getNextSubscription().getMrr();
             this.nextCurrency = bstModelDao.getNextSubscription().getCurrency();
+            this.nextBusinessActive = bstModelDao.getNextSubscription().getBusinessActive();
             this.nextStartDate = bstModelDao.getNextSubscription().getStartDate();
+            this.nextEndDate = bstModelDao.getNextSubscription().getEndDate();
             this.nextState = bstModelDao.getNextSubscription().getState().toString();
         } else {
             this.nextProductName = null;
@@ -130,330 +141,10 @@ public class BusinessSubscriptionTransition {
             this.nextPriceList = null;
             this.nextMrr = null;
             this.nextCurrency = null;
+            this.nextBusinessActive = null;
             this.nextStartDate = null;
+            this.nextEndDate = null;
             this.nextState = null;
         }
     }
-
-    public long getTotalOrdering() {
-        return totalOrdering;
-    }
-
-    public UUID getBundleId() {
-        return bundleId;
-    }
-
-    public String getExternalKey() {
-        return externalKey;
-    }
-
-    public UUID getAccountId() {
-        return accountId;
-    }
-
-    public String getAccountKey() {
-        return accountKey;
-    }
-
-    public UUID getSubscriptionId() {
-        return subscriptionId;
-    }
-
-    public DateTime getRequestedTimestamp() {
-        return requestedTimestamp;
-    }
-
-    public String getEventType() {
-        return eventType;
-    }
-
-    public String getCategory() {
-        return category;
-    }
-
-    public String getPrevProductName() {
-        return prevProductName;
-    }
-
-    public String getPrevProductType() {
-        return prevProductType;
-    }
-
-    public String getPrevProductCategory() {
-        return prevProductCategory;
-    }
-
-    public String getPrevSlug() {
-        return prevSlug;
-    }
-
-    public String getPrevPhase() {
-        return prevPhase;
-    }
-
-    public String getPrevBillingPeriod() {
-        return prevBillingPeriod;
-    }
-
-    public BigDecimal getPrevPrice() {
-        return prevPrice;
-    }
-
-    public String getPrevPriceList() {
-        return prevPriceList;
-    }
-
-    public BigDecimal getPrevMrr() {
-        return prevMrr;
-    }
-
-    public String getPrevCurrency() {
-        return prevCurrency;
-    }
-
-    public DateTime getPrevStartDate() {
-        return prevStartDate;
-    }
-
-    public String getPrevState() {
-        return prevState;
-    }
-
-    public String getNextProductName() {
-        return nextProductName;
-    }
-
-    public String getNextProductType() {
-        return nextProductType;
-    }
-
-    public String getNextProductCategory() {
-        return nextProductCategory;
-    }
-
-    public String getNextSlug() {
-        return nextSlug;
-    }
-
-    public String getNextPhase() {
-        return nextPhase;
-    }
-
-    public String getNextBillingPeriod() {
-        return nextBillingPeriod;
-    }
-
-    public BigDecimal getNextPrice() {
-        return nextPrice;
-    }
-
-    public String getNextPriceList() {
-        return nextPriceList;
-    }
-
-    public BigDecimal getNextMrr() {
-        return nextMrr;
-    }
-
-    public String getNextCurrency() {
-        return nextCurrency;
-    }
-
-    public DateTime getNextStartDate() {
-        return nextStartDate;
-    }
-
-    public String getNextState() {
-        return nextState;
-    }
-
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("BusinessSubscriptionTransition");
-        sb.append("{totalOrdering=").append(totalOrdering);
-        sb.append(", bundleId=").append(bundleId);
-        sb.append(", externalKey='").append(externalKey).append('\'');
-        sb.append(", accountId=").append(accountId);
-        sb.append(", accountKey='").append(accountKey).append('\'');
-        sb.append(", subscriptionId=").append(subscriptionId);
-        sb.append(", requestedTimestamp=").append(requestedTimestamp);
-        sb.append(", eventType='").append(eventType).append('\'');
-        sb.append(", category='").append(category).append('\'');
-        sb.append(", prevProductName='").append(prevProductName).append('\'');
-        sb.append(", prevProductType='").append(prevProductType).append('\'');
-        sb.append(", prevProductCategory='").append(prevProductCategory).append('\'');
-        sb.append(", prevSlug='").append(prevSlug).append('\'');
-        sb.append(", prevPhase='").append(prevPhase).append('\'');
-        sb.append(", prevBillingPeriod='").append(prevBillingPeriod).append('\'');
-        sb.append(", prevPrice=").append(prevPrice);
-        sb.append(", prevPriceList='").append(prevPriceList).append('\'');
-        sb.append(", prevMrr=").append(prevMrr);
-        sb.append(", prevCurrency='").append(prevCurrency).append('\'');
-        sb.append(", prevStartDate=").append(prevStartDate);
-        sb.append(", prevState='").append(prevState).append('\'');
-        sb.append(", nextProductName='").append(nextProductName).append('\'');
-        sb.append(", nextProductType='").append(nextProductType).append('\'');
-        sb.append(", nextProductCategory='").append(nextProductCategory).append('\'');
-        sb.append(", nextSlug='").append(nextSlug).append('\'');
-        sb.append(", nextPhase='").append(nextPhase).append('\'');
-        sb.append(", nextBillingPeriod='").append(nextBillingPeriod).append('\'');
-        sb.append(", nextPrice=").append(nextPrice);
-        sb.append(", nextPriceList='").append(nextPriceList).append('\'');
-        sb.append(", nextMrr=").append(nextMrr);
-        sb.append(", nextCurrency='").append(nextCurrency).append('\'');
-        sb.append(", nextStartDate=").append(nextStartDate);
-        sb.append(", nextState='").append(nextState).append('\'');
-        sb.append('}');
-        return sb.toString();
-    }
-
-    public boolean equals(final Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        final BusinessSubscriptionTransition that = (BusinessSubscriptionTransition) o;
-
-        if (totalOrdering != that.totalOrdering) {
-            return false;
-        }
-        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
-            return false;
-        }
-        if (accountKey != null ? !accountKey.equals(that.accountKey) : that.accountKey != null) {
-            return false;
-        }
-        if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
-            return false;
-        }
-        if (category != null ? !category.equals(that.category) : that.category != null) {
-            return false;
-        }
-        if (eventType != null ? !eventType.equals(that.eventType) : that.eventType != null) {
-            return false;
-        }
-        if (externalKey != null ? !externalKey.equals(that.externalKey) : that.externalKey != null) {
-            return false;
-        }
-        if (nextBillingPeriod != null ? !nextBillingPeriod.equals(that.nextBillingPeriod) : that.nextBillingPeriod != null) {
-            return false;
-        }
-        if (nextCurrency != null ? !nextCurrency.equals(that.nextCurrency) : that.nextCurrency != null) {
-            return false;
-        }
-        if (nextMrr != null ? !nextMrr.equals(that.nextMrr) : that.nextMrr != null) {
-            return false;
-        }
-        if (nextPhase != null ? !nextPhase.equals(that.nextPhase) : that.nextPhase != null) {
-            return false;
-        }
-        if (nextPrice != null ? !nextPrice.equals(that.nextPrice) : that.nextPrice != null) {
-            return false;
-        }
-        if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
-            return false;
-        }
-        if (nextProductCategory != null ? !nextProductCategory.equals(that.nextProductCategory) : that.nextProductCategory != null) {
-            return false;
-        }
-        if (nextProductName != null ? !nextProductName.equals(that.nextProductName) : that.nextProductName != null) {
-            return false;
-        }
-        if (nextProductType != null ? !nextProductType.equals(that.nextProductType) : that.nextProductType != null) {
-            return false;
-        }
-        if (nextSlug != null ? !nextSlug.equals(that.nextSlug) : that.nextSlug != null) {
-            return false;
-        }
-        if (nextStartDate != null ? !nextStartDate.equals(that.nextStartDate) : that.nextStartDate != null) {
-            return false;
-        }
-        if (nextState != null ? !nextState.equals(that.nextState) : that.nextState != null) {
-            return false;
-        }
-        if (prevBillingPeriod != null ? !prevBillingPeriod.equals(that.prevBillingPeriod) : that.prevBillingPeriod != null) {
-            return false;
-        }
-        if (prevCurrency != null ? !prevCurrency.equals(that.prevCurrency) : that.prevCurrency != null) {
-            return false;
-        }
-        if (prevMrr != null ? !prevMrr.equals(that.prevMrr) : that.prevMrr != null) {
-            return false;
-        }
-        if (prevPhase != null ? !prevPhase.equals(that.prevPhase) : that.prevPhase != null) {
-            return false;
-        }
-        if (prevPrice != null ? !prevPrice.equals(that.prevPrice) : that.prevPrice != null) {
-            return false;
-        }
-        if (prevPriceList != null ? !prevPriceList.equals(that.prevPriceList) : that.prevPriceList != null) {
-            return false;
-        }
-        if (prevProductCategory != null ? !prevProductCategory.equals(that.prevProductCategory) : that.prevProductCategory != null) {
-            return false;
-        }
-        if (prevProductName != null ? !prevProductName.equals(that.prevProductName) : that.prevProductName != null) {
-            return false;
-        }
-        if (prevProductType != null ? !prevProductType.equals(that.prevProductType) : that.prevProductType != null) {
-            return false;
-        }
-        if (prevSlug != null ? !prevSlug.equals(that.prevSlug) : that.prevSlug != null) {
-            return false;
-        }
-        if (prevStartDate != null ? !prevStartDate.equals(that.prevStartDate) : that.prevStartDate != null) {
-            return false;
-        }
-        if (prevState != null ? !prevState.equals(that.prevState) : that.prevState != null) {
-            return false;
-        }
-        if (requestedTimestamp != null ? !requestedTimestamp.equals(that.requestedTimestamp) : that.requestedTimestamp != null) {
-            return false;
-        }
-        if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
-            return false;
-        }
-
-        return true;
-    }
-
-    public int hashCode() {
-        int result = (int) (totalOrdering ^ (totalOrdering >>> 32));
-        result = 31 * result + (bundleId != null ? bundleId.hashCode() : 0);
-        result = 31 * result + (externalKey != null ? externalKey.hashCode() : 0);
-        result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
-        result = 31 * result + (accountKey != null ? accountKey.hashCode() : 0);
-        result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
-        result = 31 * result + (requestedTimestamp != null ? requestedTimestamp.hashCode() : 0);
-        result = 31 * result + (eventType != null ? eventType.hashCode() : 0);
-        result = 31 * result + (category != null ? category.hashCode() : 0);
-        result = 31 * result + (prevProductName != null ? prevProductName.hashCode() : 0);
-        result = 31 * result + (prevProductType != null ? prevProductType.hashCode() : 0);
-        result = 31 * result + (prevProductCategory != null ? prevProductCategory.hashCode() : 0);
-        result = 31 * result + (prevSlug != null ? prevSlug.hashCode() : 0);
-        result = 31 * result + (prevPhase != null ? prevPhase.hashCode() : 0);
-        result = 31 * result + (prevBillingPeriod != null ? prevBillingPeriod.hashCode() : 0);
-        result = 31 * result + (prevPrice != null ? prevPrice.hashCode() : 0);
-        result = 31 * result + (prevPriceList != null ? prevPriceList.hashCode() : 0);
-        result = 31 * result + (prevMrr != null ? prevMrr.hashCode() : 0);
-        result = 31 * result + (prevCurrency != null ? prevCurrency.hashCode() : 0);
-        result = 31 * result + (prevStartDate != null ? prevStartDate.hashCode() : 0);
-        result = 31 * result + (prevState != null ? prevState.hashCode() : 0);
-        result = 31 * result + (nextProductName != null ? nextProductName.hashCode() : 0);
-        result = 31 * result + (nextProductType != null ? nextProductType.hashCode() : 0);
-        result = 31 * result + (nextProductCategory != null ? nextProductCategory.hashCode() : 0);
-        result = 31 * result + (nextSlug != null ? nextSlug.hashCode() : 0);
-        result = 31 * result + (nextPhase != null ? nextPhase.hashCode() : 0);
-        result = 31 * result + (nextBillingPeriod != null ? nextBillingPeriod.hashCode() : 0);
-        result = 31 * result + (nextPrice != null ? nextPrice.hashCode() : 0);
-        result = 31 * result + (nextPriceList != null ? nextPriceList.hashCode() : 0);
-        result = 31 * result + (nextMrr != null ? nextMrr.hashCode() : 0);
-        result = 31 * result + (nextCurrency != null ? nextCurrency.hashCode() : 0);
-        result = 31 * result + (nextStartDate != null ? nextStartDate.hashCode() : 0);
-        result = 31 * result + (nextState != null ? nextState.hashCode() : 0);
-        return result;
-    }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessAnalyticsBase.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessAnalyticsBase.java
index 20a0d02..5889dee 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessAnalyticsBase.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessAnalyticsBase.java
@@ -113,6 +113,11 @@ public abstract class BusinessAnalyticsBase {
         return entitlementUserApi.getSubscriptionsForBundle(bundleId, context);
     }
 
+    protected List<SubscriptionBundle> getSubscriptionBundlesForAccount(final UUID accountId, final TenantContext context) throws AnalyticsRefreshException {
+        final EntitlementUserApi entitlementUserApi = getEntitlementUserApi();
+        return entitlementUserApi.getBundlesForAccount(accountId, context);
+    }
+
     protected Subscription getSubscription(final UUID subscriptionId, final TenantContext context) throws AnalyticsRefreshException {
         final EntitlementUserApi entitlementUserApi = getEntitlementUserApi();
 
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessSubscriptionTransitionDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessSubscriptionTransitionDao.java
index 526aaa5..872ff0a 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessSubscriptionTransitionDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessSubscriptionTransitionDao.java
@@ -16,210 +16,118 @@
 
 package com.ning.billing.osgi.bundles.analytics;
 
-import java.util.ArrayList;
 import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
+
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
 
 import com.ning.billing.account.api.Account;
-import com.ning.billing.account.api.AccountApiException;
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.entitlement.api.SubscriptionTransitionType;
-import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessSubscriptionTransitionSqlDao;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessAnalyticsSqlDao;
 import com.ning.billing.osgi.bundles.analytics.model.BusinessSubscription;
 import com.ning.billing.osgi.bundles.analytics.model.BusinessSubscriptionEvent;
 import com.ning.billing.osgi.bundles.analytics.model.BusinessSubscriptionTransitionModelDao;
 import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.util.events.EffectiveSubscriptionInternalEvent;
-import com.ning.billing.util.events.SubscriptionInternalEvent;
 import com.ning.killbill.osgi.libs.killbill.OSGIKillbillAPI;
 import com.ning.killbill.osgi.libs.killbill.OSGIKillbillDataSource;
 import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
 
 public class BusinessSubscriptionTransitionDao extends BusinessAnalyticsDaoBase {
 
-    public BusinessSubscriptionTransitionDao(final OSGIKillbillLogService logService, final OSGIKillbillAPI osgiKillbillAPI,
+    public BusinessSubscriptionTransitionDao(final OSGIKillbillLogService logService,
+                                             final OSGIKillbillAPI osgiKillbillAPI,
                                              final OSGIKillbillDataSource osgiKillbillDataSource) {
         super(logService, osgiKillbillAPI, osgiKillbillDataSource);
     }
 
-    public void update(final UUID bundleId, final CallContext context) throws AnalyticsRefreshException {
-        final SubscriptionBundle bundle = getSubscriptionBundle(bundleId, context);
-        final Collection<Subscription> subscriptions = getSubscriptionsForBundle(bundleId, context);
-        final Account account = getAccount(bundle.getAccountId(), context);
-        final Currency currency = account.getCurrency();
+    public void update(final UUID accountId, final CallContext context) throws AnalyticsRefreshException {
+        final Account account = getAccount(accountId, context);
 
-        sqlDao.inTransaction(new Transaction<Void, BusinessSubscriptionTransitionSqlDao>() {
-            @Override
-            public Void inTransaction(final BusinessSubscriptionTransitionSqlDao transactional, final TransactionStatus status) throws Exception {
-                transactional.deleteTransitionsForBundle(bundleId.toString(), context);
-
-                final ArrayList<BusinessSubscriptionTransitionModelDao> transitions = new ArrayList<BusinessSubscriptionTransitionModelDao>();
-                for (final Subscription subscription : subscriptions) {
-                    // TODO remove API call from within transaction, although this is NOT a real issue as this call wil not hit the DB
-                    for (final EffectiveSubscriptionInternalEvent event : entitlementApi.getAllTransitions(subscription, context)) {
-                        final BusinessSubscriptionEvent businessEvent = getBusinessSubscriptionFromEvent(event);
-                        if (businessEvent == null) {
-                            continue;
-                        }
-
-                        final BusinessSubscription prevSubscription = createPreviousBusinessSubscription(event, businessEvent, transitions, currency);
-                        final BusinessSubscription nextSubscription = createNextBusinessSubscription(event, businessEvent, currency);
-                        final BusinessSubscriptionTransitionModelDao transition = new BusinessSubscriptionTransitionModelDao(
-                                event.getTotalOrdering(),
-                                bundleId,
-                                bundle.getExternalKey(),
-                                bundle.getAccountId(),
-                                account.getExternalKey(),
-                                subscription.getId(),
-                                event.getRequestedTransitionTime(),
-                                businessEvent,
-                                prevSubscription,
-                                nextSubscription
-                        );
-
-                        transactional.createTransition(transition, context);
-                        transitions.add(transition);
-
-                        // We need to manually add the system cancel event
-                        if (SubscriptionTransitionType.CANCEL.equals(event.getTransitionType())) {
-                            final BusinessSubscriptionTransitionModelDao systemCancelTransition = new BusinessSubscriptionTransitionModelDao(
-                                    event.getTotalOrdering(),
-                                    bundleId,
-                                    bundle.getExternalKey(),
-                                    bundle.getAccountId(),
-                                    account.getExternalKey(),
-                                    subscription.getId(),
-                                    // Note! The system cancel event requested time is the effective time when the subscription
-                                    // is cancelled, which is the effective time of the cancel event
-                                    event.getEffectiveTransitionTime(),
-                                    new BusinessSubscriptionEvent(BusinessSubscriptionEvent.EventType.SYSTEM_CANCEL, businessEvent.getCategory()),
-                                    prevSubscription,
-                                    nextSubscription
-                            );
-                            transactional.createTransition(systemCancelTransition, context);
-                            transitions.add(systemCancelTransition);
-                        }
-                    }
-                }
+        // Recompute all invoices and invoice items
+        final Collection<BusinessSubscriptionTransitionModelDao> bsts = createBusinessSubscriptionTransitions(account, context);
 
+        sqlDao.inTransaction(new Transaction<Void, BusinessAnalyticsSqlDao>() {
+            @Override
+            public Void inTransaction(final BusinessAnalyticsSqlDao transactional, final TransactionStatus status) throws Exception {
+                updateInTransaction(bsts, transactional, context);
                 return null;
             }
         });
     }
 
-    private BusinessSubscriptionEvent getBusinessSubscriptionFromEvent(final SubscriptionInternalEvent event) throws AccountApiException, EntitlementUserApiException {
-        switch (event.getTransitionType()) {
-            // A subscription enters either through migration or as newly created subscription
-            case MIGRATE_ENTITLEMENT:
-                return subscriptionMigrated(event);
-            case CREATE:
-                return subscriptionCreated(event);
-            case RE_CREATE:
-                return subscriptionRecreated(event);
-            case TRANSFER:
-                return subscriptionTransfered(event);
-            case CANCEL:
-                return subscriptionCancelled(event);
-            case CHANGE:
-                return subscriptionChanged(event);
-            case PHASE:
-                return subscriptionPhaseChanged(event);
-            // TODO - should we really ignore these?
-            case MIGRATE_BILLING:
-            case UNCANCEL:
-                return null;
-            default:
-                log.warn("Unexpected event type " + event.getTransitionType());
-                return null;
+    public void updateInTransaction(final Collection<BusinessSubscriptionTransitionModelDao> bsts, final BusinessAnalyticsSqlDao transactional, final CallContext context) {
+        if (bsts.size() == 0) {
+            return;
         }
-    }
 
-    private BusinessSubscriptionEvent subscriptionMigrated(final SubscriptionInternalEvent created) throws AccountApiException, EntitlementUserApiException {
-        return BusinessSubscriptionEvent.subscriptionMigrated(created.getNextPlan(), catalogService.getFullCatalog(), created.getEffectiveTransitionTime(), created.getSubscriptionStartDate());
-    }
+        final BusinessSubscriptionTransitionModelDao firstBst = bsts.iterator().next();
+        transactional.deleteByAccountRecordId(firstBst.getTableName(), firstBst.getAccountRecordId(), firstBst.getTenantRecordId(), context);
 
-    private BusinessSubscriptionEvent subscriptionCreated(final SubscriptionInternalEvent created) throws AccountApiException, EntitlementUserApiException {
-        return BusinessSubscriptionEvent.subscriptionCreated(created.getNextPlan(), catalogService.getFullCatalog(), created.getEffectiveTransitionTime(), created.getSubscriptionStartDate());
+        for (final BusinessSubscriptionTransitionModelDao bst : bsts) {
+            transactional.create(bst.getTableName(), bst, context);
+        }
     }
 
-    private BusinessSubscriptionEvent subscriptionRecreated(final SubscriptionInternalEvent recreated) throws AccountApiException, EntitlementUserApiException {
-        return BusinessSubscriptionEvent.subscriptionRecreated(recreated.getNextPlan(), catalogService.getFullCatalog(), recreated.getEffectiveTransitionTime(), recreated.getSubscriptionStartDate());
-    }
+    private Collection<BusinessSubscriptionTransitionModelDao> createBusinessSubscriptionTransitions(final Account account, final CallContext context) throws AnalyticsRefreshException {
+        final Collection<BusinessSubscriptionTransitionModelDao> bsts = new LinkedList<BusinessSubscriptionTransitionModelDao>();
 
-    private BusinessSubscriptionEvent subscriptionTransfered(final SubscriptionInternalEvent transfered) throws AccountApiException, EntitlementUserApiException {
-        return BusinessSubscriptionEvent.subscriptionTransfered(transfered.getNextPlan(), catalogService.getFullCatalog(), transfered.getEffectiveTransitionTime(), transfered.getSubscriptionStartDate());
-    }
+        final List<SubscriptionBundle> bundles = getSubscriptionBundlesForAccount(account.getId(), context);
+        for (final SubscriptionBundle bundle : bundles) {
+            final Collection<Subscription> subscriptions = getSubscriptionsForBundle(bundle.getId(), context);
+            for (final Subscription subscription : subscriptions) {
+                // TODO
+                final List<SubscriptionTransition> transitions = new LinkedList<SubscriptionTransition>();
 
-    private BusinessSubscriptionEvent subscriptionCancelled(final SubscriptionInternalEvent cancelled) throws AccountApiException, EntitlementUserApiException {
-        // cancelled.getNextPlan() is null here - need to look at the previous one to create the correct event name
-        return BusinessSubscriptionEvent.subscriptionCancelled(cancelled.getPreviousPlan(), catalogService.getFullCatalog(), cancelled.getEffectiveTransitionTime(), cancelled.getSubscriptionStartDate());
-    }
-
-    private BusinessSubscriptionEvent subscriptionChanged(final SubscriptionInternalEvent changed) throws AccountApiException, EntitlementUserApiException {
-        return BusinessSubscriptionEvent.subscriptionChanged(changed.getNextPlan(), catalogService.getFullCatalog(), changed.getEffectiveTransitionTime(), changed.getSubscriptionStartDate());
-    }
+                BusinessSubscriptionTransitionModelDao prevBst = null;
 
-    private BusinessSubscriptionEvent subscriptionPhaseChanged(final SubscriptionInternalEvent phaseChanged) throws AccountApiException, EntitlementUserApiException {
-        return BusinessSubscriptionEvent.subscriptionPhaseChanged(phaseChanged.getNextPlan(), phaseChanged.getNextState(), catalogService.getFullCatalog(), phaseChanged.getEffectiveTransitionTime(), phaseChanged.getSubscriptionStartDate());
-    }
-
-    private BusinessSubscription createNextBusinessSubscription(final EffectiveSubscriptionInternalEvent event, final BusinessSubscriptionEvent businessEvent, final Currency currency) {
-        final BusinessSubscription nextSubscription;
-        if (BusinessSubscriptionEvent.EventType.CANCEL.equals(businessEvent.getEventType()) ||
-            BusinessSubscriptionEvent.EventType.SYSTEM_CANCEL.equals(businessEvent.getEventType())) {
-            nextSubscription = null;
-        } else {
-            nextSubscription = new BusinessSubscription(event.getNextPriceList(), event.getNextPlan(), event.getNextPhase(),
-                                                        currency, event.getEffectiveTransitionTime(), event.getNextState(),
-                                                        catalogService.getFullCatalog());
+                // Ordered for us by entitlement
+                for (final SubscriptionTransition transition : transitions) {
+                    final BusinessSubscriptionTransitionModelDao bst = createBusinessSubscriptionTransition(account, bundle, transition, prevBst, context);
+                    if (bst != null) {
+                        bsts.add(bst);
+                        prevBst = bst;
+                    }
+                }
+            }
         }
 
-        return nextSubscription;
+        return bsts;
     }
 
-    private BusinessSubscription createPreviousBusinessSubscription(final EffectiveSubscriptionInternalEvent event,
-                                                                    final BusinessSubscriptionEvent businessEvent,
-                                                                    final ArrayList<BusinessSubscriptionTransitionModelDao> transitions,
-                                                                    final Currency currency) {
-        if (BusinessSubscriptionEvent.EventType.MIGRATE.equals(businessEvent.getEventType()) ||
-            BusinessSubscriptionEvent.EventType.ADD.equals(businessEvent.getEventType()) ||
-            BusinessSubscriptionEvent.EventType.RE_ADD.equals(businessEvent.getEventType()) ||
-            BusinessSubscriptionEvent.EventType.TRANSFER.equals(businessEvent.getEventType())) {
+    private BusinessSubscriptionTransitionModelDao createBusinessSubscriptionTransition(final Account account,
+                                                                                        final SubscriptionBundle subscriptionBundle,
+                                                                                        final SubscriptionTransition subscriptionTransition,
+                                                                                        @Nullable final BusinessSubscriptionTransitionModelDao prevBst,
+                                                                                        final CallContext context) throws AnalyticsRefreshException {
+        final BusinessSubscriptionEvent businessEvent = BusinessSubscriptionEvent.fromTransition(subscriptionTransition);
+        if (businessEvent == null) {
             return null;
         }
 
-        final BusinessSubscriptionTransitionModelDao prevTransition = getPreviousBusinessSubscriptionTransitionForEvent(event, transitions);
-        return new BusinessSubscription(event.getPreviousPriceList(), event.getPreviousPlan(), event.getPreviousPhase(),
-                                        currency, prevTransition.getNextSubscription().getStartDate(), event.getPreviousState(),
-                                        catalogService.getFullCatalog());
-    }
-
-    private BusinessSubscriptionTransitionModelDao getPreviousBusinessSubscriptionTransitionForEvent(final EffectiveSubscriptionInternalEvent event,
-                                                                                                     final ArrayList<BusinessSubscriptionTransitionModelDao> transitions) {
-        BusinessSubscriptionTransitionModelDao transition = null;
-        for (final BusinessSubscriptionTransitionModelDao candidate : transitions) {
-            final BusinessSubscription nextSubscription = candidate.getNextSubscription();
-            if (nextSubscription == null || !nextSubscription.getStartDate().isBefore(event.getEffectiveTransitionTime())) {
-                continue;
-            }
-
-            if (candidate.getSubscriptionId().equals(event.getSubscriptionId())) {
-                transition = candidate;
-            }
-        }
-
-        if (transition == null) {
-            log.error("Unable to retrieve the previous transition - THIS SHOULD NEVER HAPPEN");
-            // Fall back to the latest one?
-            transition = transitions.get(transitions.size() - 1);
-        }
-
-        return transition;
+        final BusinessSubscription nextSubscription = new BusinessSubscription(subscriptionTransition.getNextPlan(),
+                                                                               subscriptionTransition.getNextPhase(),
+                                                                               subscriptionTransition.getNextPriceList(),
+                                                                               account.getCurrency(),
+                                                                               subscriptionTransition.getEffectiveTransitionTime(),
+                                                                               subscriptionTransition.getNextState());
+
+        return new BusinessSubscriptionTransitionModelDao(account,
+                                                          subscriptionBundle,
+                                                          subscriptionTransition,
+                                                          subscriptionTransition.getRequestedTransitionTime(),
+                                                          businessEvent,
+                                                          prevBst == null ? null : prevBst.getNextSubscription(),
+                                                          nextSubscription,
+                                                          // TODO
+                                                          null,
+                                                          null,
+                                                          null);
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessSubscription.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessSubscription.java
index 23d47a0..574af8c 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessSubscription.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessSubscription.java
@@ -19,17 +19,19 @@ package com.ning.billing.osgi.bundles.analytics.model;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.Catalog;
 import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.Duration;
 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;
 import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.osgi.bundles.analytics.utils.Rounder;
 
 import static com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
@@ -56,128 +58,12 @@ public class BusinessSubscription {
     private final DateTime startDate;
     private final DateTime endDate;
 
-    public BusinessSubscription(final String productName,
-                                final String productType,
-                                final ProductCategory productCategory,
-                                final String slug,
-                                final String phase,
-                                final String billingPeriod,
-                                final BigDecimal price,
-                                final String priceList,
-                                final BigDecimal mrr,
-                                final String currency,
-                                final SubscriptionState state,
-                                final Boolean businessActive,
-                                final DateTime startDate,
-                                final DateTime endDate) {
-        this.productName = productName;
-        this.productType = productType;
-        this.productCategory = productCategory;
-        this.slug = slug;
-        this.phase = phase;
-        this.billingPeriod = billingPeriod;
-        this.price = price;
-        this.priceList = priceList;
-        this.mrr = mrr;
-        this.currency = currency;
-        this.state = state;
-        this.businessActive = businessActive;
-        this.startDate = startDate;
-        this.endDate = endDate;
-    }
-
-    /**
-     * For unit tests only.
-     * <p/>
-     * You can't really use this constructor in real life because the start date is likely not the one you want (you likely
-     * want the phase start date).
-     *
-     * @param subscription Subscription to use as a model
-     * @param currency     ACCOUNT currency
-     * @param catalog      Catalog to use
-     */
-    BusinessSubscription(final Subscription subscription, final Currency currency, final Catalog catalog) {
-        this(subscription.getCurrentPriceList() == null ? null : subscription.getCurrentPriceList().getName(),
-             subscription.getCurrentPlan().getName(), subscription.getCurrentPhase().getName(), currency,
-             subscription.getStartDate(), subscription.getState(), catalog);
-    }
-
-    public BusinessSubscription(final String priceList, final String currentPlan, final String currentPhase, final Currency currency,
-                                final DateTime startDate, final SubscriptionState state, final Catalog catalog) {
-        Plan thePlan = null;
-        PlanPhase thePhase = null;
-        try {
-            thePlan = (currentPlan != null) ? catalog.findPlan(currentPlan, new DateTime(), startDate) : null;
-            thePhase = (currentPhase != null) ? catalog.findPhase(currentPhase, new DateTime(), startDate) : null;
-        } catch (CatalogApiException ignored) {
-        }
-
-        this.priceList = priceList;
-
-        // Record plan information
-        if (currentPlan != null && thePlan != null && thePlan.getProduct() != null) {
-            final Product product = thePlan.getProduct();
-            productName = product.getName();
-            productCategory = product.getCategory();
-            // TODO - we should keep the product type
-            productType = product.getCatalogName();
-        } else {
-            productName = null;
-            productCategory = null;
-            productType = null;
-        }
-
-        // Record phase information
-        if (currentPhase != null && thePhase != null) {
-            slug = thePhase.getName();
-
-            if (thePhase.getPhaseType() != null) {
-                phase = thePhase.getPhaseType().toString();
-            } else {
-                phase = null;
-            }
-
-            if (thePhase.getBillingPeriod() != null) {
-                billingPeriod = thePhase.getBillingPeriod().toString();
-            } else {
-                billingPeriod = null;
-            }
-
-            if (thePhase.getRecurringPrice() != null) {
-                //TODO check if this is the right way to handle exception
-                BigDecimal tmpPrice;
-                try {
-                    tmpPrice = thePhase.getRecurringPrice().getPrice(USD);
-                } catch (CatalogApiException e) {
-                    tmpPrice = new BigDecimal(0);
-                }
-                price = tmpPrice;
-                mrr = getMrrFromBillingPeriod(thePhase.getBillingPeriod(), price);
-            } else {
-                price = BigDecimal.ZERO;
-                mrr = BigDecimal.ZERO;
-            }
-        } else {
-            slug = null;
-            phase = null;
-            billingPeriod = null;
-            price = BigDecimal.ZERO;
-            mrr = BigDecimal.ZERO;
-        }
-
-        if (currency != null) {
-            this.currency = currency.toString();
-        } else {
-            this.currency = null;
-        }
-
-        this.startDate = startDate;
-        this.state = state;
-    }
+    public BusinessSubscription(@Nullable final Plan currentPlan, @Nullable final PlanPhase currentPhase, @Nullable final PriceList priceList,
+                                final Currency currency, final DateTime startDate, final SubscriptionState state) {
+        // TODO
+        businessActive = true;
 
-    public BusinessSubscription(final String priceList, final Plan currentPlan, final PlanPhase currentPhase, final Currency currency,
-                                final DateTime startDate, final SubscriptionState state) {
-        this.priceList = priceList;
+        this.priceList = priceList == null ? null : priceList.getName();
 
         // Record plan information
         if (currentPlan != null && currentPlan.getProduct() != null) {
@@ -209,15 +95,19 @@ public class BusinessSubscription {
             }
 
             if (currentPhase.getRecurringPrice() != null) {
-                //TODO check if this is the right way to handle exception
                 BigDecimal tmpPrice;
                 try {
                     tmpPrice = currentPhase.getRecurringPrice().getPrice(USD);
                 } catch (CatalogApiException e) {
-                    tmpPrice = new BigDecimal(0);
+                    tmpPrice = null;
                 }
+
                 price = tmpPrice;
-                mrr = getMrrFromBillingPeriod(currentPhase.getBillingPeriod(), price);
+                if (tmpPrice != null) {
+                    mrr = getMrrFromBillingPeriod(currentPhase.getBillingPeriod(), price);
+                } else {
+                    mrr = null;
+                }
             } else {
                 price = BigDecimal.ZERO;
                 mrr = BigDecimal.ZERO;
@@ -237,6 +127,12 @@ public class BusinessSubscription {
         }
 
         this.startDate = startDate;
+        if (currentPhase != null) {
+            final Duration duration = currentPhase.getDuration();
+            this.endDate = duration == null ? null : startDate.plus(duration.toJodaPeriod());
+        } else {
+            this.endDate = null;
+        }
         this.state = state;
     }
 
@@ -288,10 +184,18 @@ public class BusinessSubscription {
         return slug;
     }
 
+    public Boolean getBusinessActive() {
+        return businessActive;
+    }
+
     public DateTime getStartDate() {
         return startDate;
     }
 
+    public DateTime getEndDate() {
+        return endDate;
+    }
+
     public SubscriptionState getState() {
         return state;
     }
@@ -308,18 +212,20 @@ public class BusinessSubscription {
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         sb.append("BusinessSubscription");
-        sb.append("{billingPeriod='").append(billingPeriod).append('\'');
-        sb.append(", productName='").append(productName).append('\'');
+        sb.append("{productName='").append(productName).append('\'');
         sb.append(", productType='").append(productType).append('\'');
         sb.append(", productCategory=").append(productCategory);
         sb.append(", slug='").append(slug).append('\'');
         sb.append(", phase='").append(phase).append('\'');
+        sb.append(", billingPeriod='").append(billingPeriod).append('\'');
         sb.append(", price=").append(price);
-        sb.append(", priceList=").append(priceList);
+        sb.append(", priceList='").append(priceList).append('\'');
         sb.append(", mrr=").append(mrr);
         sb.append(", currency='").append(currency).append('\'');
-        sb.append(", startDate=").append(startDate);
         sb.append(", state=").append(state);
+        sb.append(", businessActive=").append(businessActive);
+        sb.append(", startDate=").append(startDate);
+        sb.append(", endDate=").append(endDate);
         sb.append('}');
         return sb.toString();
     }
@@ -338,22 +244,28 @@ public class BusinessSubscription {
         if (billingPeriod != null ? !billingPeriod.equals(that.billingPeriod) : that.billingPeriod != null) {
             return false;
         }
+        if (businessActive != null ? !businessActive.equals(that.businessActive) : that.businessActive != null) {
+            return false;
+        }
         if (currency != null ? !currency.equals(that.currency) : that.currency != null) {
             return false;
         }
-        if (mrr != null ? !(Rounder.round(mrr) == Rounder.round(that.mrr)) : that.mrr != null) {
+        if (endDate != null ? !endDate.equals(that.endDate) : that.endDate != null) {
+            return false;
+        }
+        if (mrr != null ? !mrr.equals(that.mrr) : that.mrr != null) {
             return false;
         }
         if (phase != null ? !phase.equals(that.phase) : that.phase != null) {
             return false;
         }
-        if (price != null ? !(Rounder.round(price) == Rounder.round(that.price)) : that.price != null) {
+        if (price != null ? !price.equals(that.price) : that.price != null) {
             return false;
         }
         if (priceList != null ? !priceList.equals(that.priceList) : that.priceList != null) {
             return false;
         }
-        if (productCategory != null ? !productCategory.equals(that.productCategory) : that.productCategory != null) {
+        if (productCategory != that.productCategory) {
             return false;
         }
         if (productName != null ? !productName.equals(that.productName) : that.productName != null) {
@@ -382,13 +294,15 @@ public class BusinessSubscription {
         result = 31 * result + (productCategory != null ? productCategory.hashCode() : 0);
         result = 31 * result + (slug != null ? slug.hashCode() : 0);
         result = 31 * result + (phase != null ? phase.hashCode() : 0);
+        result = 31 * result + (billingPeriod != null ? billingPeriod.hashCode() : 0);
         result = 31 * result + (price != null ? price.hashCode() : 0);
         result = 31 * result + (priceList != null ? priceList.hashCode() : 0);
         result = 31 * result + (mrr != null ? mrr.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
-        result = 31 * result + (startDate != null ? startDate.hashCode() : 0);
         result = 31 * result + (state != null ? state.hashCode() : 0);
-        result = 31 * result + (billingPeriod != null ? billingPeriod.hashCode() : 0);
+        result = 31 * result + (businessActive != null ? businessActive.hashCode() : 0);
+        result = 31 * result + (startDate != null ? startDate.hashCode() : 0);
+        result = 31 * result + (endDate != null ? endDate.hashCode() : 0);
         return result;
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessSubscriptionEvent.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessSubscriptionEvent.java
index 5e8b40b..35a3a98 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessSubscriptionEvent.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessSubscriptionEvent.java
@@ -18,15 +18,10 @@ package com.ning.billing.osgi.bundles.analytics.model;
 
 import javax.annotation.Nullable;
 
-import org.joda.time.DateTime;
-
-import com.ning.billing.catalog.api.Catalog;
-import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.Product;
 import com.ning.billing.catalog.api.ProductCategory;
-
-import static com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
 
 /**
  * Describe an event associated with a transition between two BusinessSubscription
@@ -80,46 +75,61 @@ public class BusinessSubscriptionEvent {
         return eventType;
     }
 
-    public static BusinessSubscriptionEvent subscriptionMigrated(final String plan, final Catalog catalog, final DateTime eventTime, final DateTime subscriptionCreationDate) {
-        return eventFromType(EventType.MIGRATE, plan, catalog, eventTime, subscriptionCreationDate);
+    public static BusinessSubscriptionEvent fromTransition(final SubscriptionTransition transition) {
+        switch (transition.getTransitionType()) {
+            // A subscription enters either through migration or as newly created subscription
+            case MIGRATE_ENTITLEMENT:
+                return subscriptionMigrated(transition.getNextPlan());
+            case CREATE:
+                return subscriptionCreated(transition.getNextPlan());
+            case RE_CREATE:
+                return subscriptionRecreated(transition.getNextPlan());
+            case TRANSFER:
+                return subscriptionTransfered(transition.getNextPlan());
+            case CANCEL:
+                return subscriptionCancelled(transition.getNextPlan());
+            case CHANGE:
+                return subscriptionChanged(transition.getNextPlan());
+            case PHASE:
+                return subscriptionPhaseChanged(transition.getNextPlan());
+            // TODO - should we really ignore these?
+            case MIGRATE_BILLING:
+            case UNCANCEL:
+            default:
+                return null;
+        }
     }
 
-    public static BusinessSubscriptionEvent subscriptionCreated(final String plan, final Catalog catalog, final DateTime eventTime, final DateTime subscriptionCreationDate) {
-        return eventFromType(EventType.ADD, plan, catalog, eventTime, subscriptionCreationDate);
+    private static BusinessSubscriptionEvent subscriptionMigrated(final Plan plan) {
+        return eventFromType(EventType.MIGRATE, plan);
     }
 
-    public static BusinessSubscriptionEvent subscriptionCancelled(final String plan, final Catalog catalog, final DateTime eventTime, final DateTime subscriptionCreationDate) {
-        return eventFromType(EventType.CANCEL, plan, catalog, eventTime, subscriptionCreationDate);
+    private static BusinessSubscriptionEvent subscriptionCreated(final Plan plan) {
+        return eventFromType(EventType.ADD, plan);
     }
 
-    public static BusinessSubscriptionEvent subscriptionChanged(final String plan, final Catalog catalog, final DateTime eventTime, final DateTime subscriptionCreationDate) {
-        return eventFromType(EventType.CHANGE, plan, catalog, eventTime, subscriptionCreationDate);
+    private static BusinessSubscriptionEvent subscriptionCancelled(final Plan plan) {
+        return eventFromType(EventType.CANCEL, plan);
     }
 
-    public static BusinessSubscriptionEvent subscriptionRecreated(final String plan, final Catalog catalog, final DateTime eventTime, final DateTime subscriptionCreationDate) {
-        return eventFromType(EventType.RE_ADD, plan, catalog, eventTime, subscriptionCreationDate);
+    private static BusinessSubscriptionEvent subscriptionChanged(final Plan plan) {
+        return eventFromType(EventType.CHANGE, plan);
     }
 
-    public static BusinessSubscriptionEvent subscriptionTransfered(final String plan, final Catalog catalog, final DateTime eventTime, final DateTime subscriptionCreationDate) {
-        return eventFromType(EventType.TRANSFER, plan, catalog, eventTime, subscriptionCreationDate);
+    private static BusinessSubscriptionEvent subscriptionRecreated(final Plan plan) {
+        return eventFromType(EventType.RE_ADD, plan);
     }
 
-    public static BusinessSubscriptionEvent subscriptionPhaseChanged(final String plan, final SubscriptionState state, final Catalog catalog, final DateTime eventTime, final DateTime subscriptionCreationDate) {
-        if (state != null && state.equals(SubscriptionState.CANCELLED)) {
-            return eventFromType(EventType.SYSTEM_CANCEL, plan, catalog, eventTime, subscriptionCreationDate);
-        } else {
-            return eventFromType(EventType.SYSTEM_CHANGE, plan, catalog, eventTime, subscriptionCreationDate);
-        }
+    private static BusinessSubscriptionEvent subscriptionTransfered(final Plan plan) {
+        return eventFromType(EventType.TRANSFER, plan);
     }
 
-    private static BusinessSubscriptionEvent eventFromType(final EventType eventType, final String plan, final Catalog catalog, final DateTime eventTime, final DateTime subscriptionCreationDate) {
-        Plan thePlan = null;
-        try {
-            thePlan = catalog.findPlan(plan, eventTime, subscriptionCreationDate);
-        } catch (CatalogApiException ignored) {
-        }
+    private static BusinessSubscriptionEvent subscriptionPhaseChanged(final Plan plan) {
+        return eventFromType(EventType.SYSTEM_CHANGE, plan);
+    }
 
-        final ProductCategory category = getTypeFromSubscription(thePlan);
+    private static BusinessSubscriptionEvent eventFromType(final EventType eventType, final Plan plan) {
+        final ProductCategory category = getTypeFromSubscription(plan);
         return new BusinessSubscriptionEvent(eventType, category);
     }
 
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessSubscriptionTransitionModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessSubscriptionTransitionModelDao.java
index cb9cc01..f962ad6 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessSubscriptionTransitionModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessSubscriptionTransitionModelDao.java
@@ -22,15 +22,16 @@ import org.joda.time.DateTime;
 
 import com.ning.billing.account.api.Account;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
-import com.ning.billing.util.events.SubscriptionInternalEvent;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
 
 /**
  * Describe a state change between two BusinessSubscription
  */
 public class BusinessSubscriptionTransitionModelDao extends BusinessModelDaoBase {
 
+    private static final String SUBSCRIPTION_TABLE_NAME = "bst";
+
     private final Long subscriptionEventRecordId;
-    private final Long totalOrdering;
     private final UUID bundleId;
     private final String bundleExternalKey;
     private final UUID subscriptionId;
@@ -40,7 +41,6 @@ public class BusinessSubscriptionTransitionModelDao extends BusinessModelDaoBase
     private final BusinessSubscription nextSubscription;
 
     public BusinessSubscriptionTransitionModelDao(final Long subscriptionEventRecordId,
-                                                  final Long totalOrdering,
                                                   final UUID bundleId,
                                                   final String bundleExternalKey,
                                                   final UUID subscriptionId,
@@ -67,7 +67,6 @@ public class BusinessSubscriptionTransitionModelDao extends BusinessModelDaoBase
               accountRecordId,
               tenantRecordId);
         this.subscriptionEventRecordId = subscriptionEventRecordId;
-        this.totalOrdering = totalOrdering;
         this.bundleId = bundleId;
         this.bundleExternalKey = bundleExternalKey;
         this.subscriptionId = subscriptionId;
@@ -79,7 +78,7 @@ public class BusinessSubscriptionTransitionModelDao extends BusinessModelDaoBase
 
     public BusinessSubscriptionTransitionModelDao(final Account account,
                                                   final SubscriptionBundle bundle,
-                                                  final SubscriptionInternalEvent subscriptionEvent,
+                                                  final SubscriptionTransition transition,
                                                   final DateTime requestedTimestamp,
                                                   final BusinessSubscriptionEvent event,
                                                   final BusinessSubscription previousSubscription,
@@ -88,10 +87,9 @@ public class BusinessSubscriptionTransitionModelDao extends BusinessModelDaoBase
                                                   final String createdReasonCode,
                                                   final String createdComments) {
         this(null /* TODO */,
-             subscriptionEvent.getTotalOrdering(),
              bundle.getId(),
              bundle.getExternalKey(),
-             subscriptionEvent.getSubscriptionId(),
+             transition.getSubscriptionId(),
              requestedTimestamp,
              event,
              previousSubscription,
@@ -107,4 +105,113 @@ public class BusinessSubscriptionTransitionModelDao extends BusinessModelDaoBase
              null,
              null);
     }
+
+    @Override
+    public String getTableName() {
+        return SUBSCRIPTION_TABLE_NAME;
+    }
+
+    public Long getSubscriptionEventRecordId() {
+        return subscriptionEventRecordId;
+    }
+
+    public UUID getBundleId() {
+        return bundleId;
+    }
+
+    public String getBundleExternalKey() {
+        return bundleExternalKey;
+    }
+
+    public UUID getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    public DateTime getRequestedTimestamp() {
+        return requestedTimestamp;
+    }
+
+    public BusinessSubscriptionEvent getEvent() {
+        return event;
+    }
+
+    public BusinessSubscription getPreviousSubscription() {
+        return previousSubscription;
+    }
+
+    public BusinessSubscription getNextSubscription() {
+        return nextSubscription;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessSubscriptionTransitionModelDao");
+        sb.append("{subscriptionEventRecordId=").append(subscriptionEventRecordId);
+        sb.append(", bundleId=").append(bundleId);
+        sb.append(", bundleExternalKey='").append(bundleExternalKey).append('\'');
+        sb.append(", subscriptionId=").append(subscriptionId);
+        sb.append(", requestedTimestamp=").append(requestedTimestamp);
+        sb.append(", event=").append(event);
+        sb.append(", previousSubscription=").append(previousSubscription);
+        sb.append(", nextSubscription=").append(nextSubscription);
+        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;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+
+        final BusinessSubscriptionTransitionModelDao that = (BusinessSubscriptionTransitionModelDao) o;
+
+        if (bundleExternalKey != null ? !bundleExternalKey.equals(that.bundleExternalKey) : that.bundleExternalKey != null) {
+            return false;
+        }
+        if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
+            return false;
+        }
+        if (event != null ? !event.equals(that.event) : that.event != null) {
+            return false;
+        }
+        if (nextSubscription != null ? !nextSubscription.equals(that.nextSubscription) : that.nextSubscription != null) {
+            return false;
+        }
+        if (previousSubscription != null ? !previousSubscription.equals(that.previousSubscription) : that.previousSubscription != null) {
+            return false;
+        }
+        if (requestedTimestamp != null ? !requestedTimestamp.equals(that.requestedTimestamp) : that.requestedTimestamp != null) {
+            return false;
+        }
+        if (subscriptionEventRecordId != null ? !subscriptionEventRecordId.equals(that.subscriptionEventRecordId) : that.subscriptionEventRecordId != null) {
+            return false;
+        }
+        if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (subscriptionEventRecordId != null ? subscriptionEventRecordId.hashCode() : 0);
+        result = 31 * result + (bundleId != null ? bundleId.hashCode() : 0);
+        result = 31 * result + (bundleExternalKey != null ? bundleExternalKey.hashCode() : 0);
+        result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
+        result = 31 * result + (requestedTimestamp != null ? requestedTimestamp.hashCode() : 0);
+        result = 31 * result + (event != null ? event.hashCode() : 0);
+        result = 31 * result + (previousSubscription != null ? previousSubscription.hashCode() : 0);
+        result = 31 * result + (nextSubscription != null ? nextSubscription.hashCode() : 0);
+        return result;
+    }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/ddl.sql b/osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/ddl.sql
index 9b16fd3..14be6f6 100644
--- a/osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/ddl.sql
+++ b/osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/ddl.sql
@@ -5,7 +5,6 @@ drop table if exists bst;
 create table bst (
   record_id int(11) unsigned not null auto_increment
 , subscription_event_record_id int(11) unsigned default null
-, total_ordering bigint default 0
 , bundle_id char(36) not null
 , bundle_external_key varchar(50) not null
 , subscription_id char(36) not null