killbill-memoizeit

analytics: first pass at new plugin implementation Signed-off-by:

4/3/2013 3:00:03 PM

Changes

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/DefaultAnalyticsService.java 66(+0 -66)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/DefaultBusinessAccount.java 180(+0 -180)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/DefaultBusinessInvoice.java 296(+0 -296)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/DefaultBusinessInvoicePayment.java 278(+0 -278)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/sanity/DefaultAnalyticsSanityApi.java 82(+0 -82)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/AnalyticsSanityDao.java 43(+0 -43)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/AnalyticsSanitySqlDao.java 62(+0 -62)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessAccountBinder.java 78(+0 -78)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessAccountFieldMapper.java 38(+0 -38)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessAccountFieldSqlDao.java 54(+0 -54)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessAccountMapper.java 52(+0 -52)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessAccountSqlDao.java 63(+0 -63)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessAccountTagMapper.java 37(+0 -37)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessAccountTagSqlDao.java 53(+0 -53)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceBinder.java 91(+0 -91)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceFieldMapper.java 34(+0 -34)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceFieldSqlDao.java 53(+0 -53)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceItemBinder.java 94(+0 -94)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceItemMapper.java 60(+0 -60)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceItemSqlDao.java 64(+0 -64)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceMapper.java 55(+0 -55)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoicePaymentBinder.java 97(+0 -97)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoicePaymentFieldMapper.java 34(+0 -34)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoicePaymentFieldSqlDao.java 53(+0 -53)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoicePaymentMapper.java 69(+0 -69)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoicePaymentSqlDao.java 56(+0 -56)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoicePaymentTagMapper.java 34(+0 -34)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoicePaymentTagSqlDao.java 52(+0 -52)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceSqlDao.java 64(+0 -64)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessOverdueStatusBinder.java 63(+0 -63)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessOverdueStatusMapper.java 43(+0 -43)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessOverdueStatusSqlDao.java 52(+0 -52)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessSubscriptionTransitionBinder.java 138(+0 -138)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessSubscriptionTransitionFieldMapper.java 39(+0 -39)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessSubscriptionTransitionFieldSqlDao.java 55(+0 -55)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessSubscriptionTransitionMapper.java 95(+0 -95)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessSubscriptionTransitionSqlDao.java 64(+0 -64)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessSubscriptionTransitionTagMapper.java 38(+0 -38)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessSubscriptionTransitionTagSqlDao.java 54(+0 -54)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/DefaultAnalyticsSanityDao.java 81(+0 -81)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessSubscriptionTransitionFieldModelDao.java 99(+0 -99)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessSubscriptionTransitionTagModelDao.java 93(+0 -93)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/setup/AnalyticsModule.java 106(+0 -106)

pom.xml 16(+16 -0)

Details

diff --git a/osgi-bundles/bundles/analytics/pom.xml b/osgi-bundles/bundles/analytics/pom.xml
index 6b2da9c..fcf710f 100644
--- a/osgi-bundles/bundles/analytics/pom.xml
+++ b/osgi-bundles/bundles/analytics/pom.xml
@@ -33,10 +33,6 @@
             <artifactId>guava</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.google.inject</groupId>
-            <artifactId>guice</artifactId>
-        </dependency>
-        <dependency>
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-api</artifactId>
             <scope>provided</scope>
@@ -50,6 +46,18 @@
             <artifactId>killbill-util</artifactId>
         </dependency>
         <dependency>
+            <groupId>com.ning.billing.commons</groupId>
+            <artifactId>killbill-embeddeddb</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing.commons</groupId>
+            <artifactId>killbill-jdbi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing.commons</groupId>
+            <artifactId>killbill-locker</artifactId>
+        </dependency>
+        <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
             <scope>provided</scope>
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/AnalyticsActivator.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/AnalyticsActivator.java
index 1510ba8..a167ff4 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/AnalyticsActivator.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/AnalyticsActivator.java
@@ -17,15 +17,10 @@
 package com.ning.billing.osgi.bundles.analytics;
 
 import org.osgi.framework.BundleContext;
-import org.skife.config.SimplePropertyConfigSource;
 
-import com.ning.billing.osgi.bundles.analytics.setup.AnalyticsModule;
 import com.ning.killbill.osgi.libs.killbill.KillbillActivatorBase;
 import com.ning.killbill.osgi.libs.killbill.OSGIKillbillEventDispatcher.OSGIKillbillEventHandler;
 
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-
 public class AnalyticsActivator extends KillbillActivatorBase {
 
     private OSGIKillbillEventHandler analyticsListener;
@@ -34,9 +29,7 @@ public class AnalyticsActivator extends KillbillActivatorBase {
     public void start(final BundleContext context) throws Exception {
         super.start(context);
 
-        final SimplePropertyConfigSource configSource = new SimplePropertyConfigSource(System.getProperties());
-        final Injector injector = Guice.createInjector(new AnalyticsModule(configSource));
-        analyticsListener = injector.getInstance(AnalyticsListener.class);
+        analyticsListener = new AnalyticsListener(logService, killbillAPI, dataSource);
     }
 
     @Override
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/AnalyticsListener.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/AnalyticsListener.java
index a62c243..0f61ebd 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/AnalyticsListener.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/AnalyticsListener.java
@@ -16,269 +16,225 @@
 
 package com.ning.billing.osgi.bundles.analytics;
 
+import java.util.UUID;
 import java.util.concurrent.Callable;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.osgi.service.log.LogService;
 
 import com.ning.billing.beatrix.bus.api.ExtBusEvent;
+import com.ning.billing.commons.locker.GlobalLock;
+import com.ning.billing.commons.locker.GlobalLocker;
+import com.ning.billing.commons.locker.mysql.MySqlGlobalLocker;
+import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallOrigin;
-import com.ning.billing.util.callcontext.InternalCallContext;
-import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.callcontext.UserType;
-import com.ning.billing.util.events.AccountChangeInternalEvent;
-import com.ning.billing.util.events.AccountCreationInternalEvent;
-import com.ning.billing.util.events.BusInternalEvent;
-import com.ning.billing.util.events.ControlTagCreationInternalEvent;
-import com.ning.billing.util.events.ControlTagDefinitionCreationInternalEvent;
-import com.ning.billing.util.events.ControlTagDefinitionDeletionInternalEvent;
-import com.ning.billing.util.events.ControlTagDeletionInternalEvent;
-import com.ning.billing.util.events.EffectiveSubscriptionInternalEvent;
-import com.ning.billing.util.events.InvoiceAdjustmentInternalEvent;
-import com.ning.billing.util.events.InvoiceCreationInternalEvent;
-import com.ning.billing.util.events.NullInvoiceInternalEvent;
-import com.ning.billing.util.events.OverdueChangeInternalEvent;
-import com.ning.billing.util.events.PaymentErrorInternalEvent;
-import com.ning.billing.util.events.PaymentInfoInternalEvent;
-import com.ning.billing.util.events.RepairEntitlementInternalEvent;
-import com.ning.billing.util.events.RequestedSubscriptionInternalEvent;
-import com.ning.billing.util.events.UserTagCreationInternalEvent;
-import com.ning.billing.util.events.UserTagDefinitionCreationInternalEvent;
-import com.ning.billing.util.events.UserTagDefinitionDeletionInternalEvent;
-import com.ning.billing.util.events.UserTagDeletionInternalEvent;
-import com.ning.billing.util.globallocker.GlobalLock;
-import com.ning.billing.util.globallocker.GlobalLocker;
-import com.ning.billing.util.globallocker.GlobalLocker.LockerType;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillAPI;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillDataSource;
 import com.ning.killbill.osgi.libs.killbill.OSGIKillbillEventDispatcher.OSGIKillbillEventHandler;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
 
-import com.google.inject.Inject;
+public class AnalyticsListener extends BusinessAnalyticsBase implements OSGIKillbillEventHandler {
 
-public class AnalyticsListener implements OSGIKillbillEventHandler {
-
-    private static final Logger log = LoggerFactory.getLogger(AnalyticsListener.class);
     private static final int NB_LOCK_TRY = 5;
 
-    private final BusinessSubscriptionTransitionDao bstDao;
     private final BusinessAccountDao bacDao;
-    private final BusinessInvoiceDao invoiceDao;
-    private final BusinessOverdueStatusDao bosDao;
+    private final BusinessSubscriptionTransitionDao bstDao;
+    private final BusinessInvoiceDao binDao;
     private final BusinessInvoicePaymentDao bipDao;
-    private final BusinessTagDao tagDao;
+    private final BusinessOverdueStatusDao bosDao;
     private final GlobalLocker locker;
-    private final InternalCallContextFactory internalCallContextFactory;
-
-    @Inject
-    public AnalyticsListener(final BusinessSubscriptionTransitionDao bstDao,
-                             final BusinessAccountDao bacDao,
-                             final BusinessInvoiceDao invoiceDao,
-                             final BusinessOverdueStatusDao bosDao,
-                             final BusinessInvoicePaymentDao bipDao,
-                             final BusinessTagDao tagDao,
-                             final GlobalLocker locker,
-                             final InternalCallContextFactory internalCallContextFactory) {
-        this.bstDao = bstDao;
-        this.bacDao = bacDao;
-        this.invoiceDao = invoiceDao;
-        this.bosDao = bosDao;
-        this.bipDao = bipDao;
-        this.tagDao = tagDao;
-        this.locker = locker;
-        this.internalCallContextFactory = internalCallContextFactory;
+
+    public AnalyticsListener(final OSGIKillbillLogService logService, final OSGIKillbillAPI osgiKillbillAPI, final OSGIKillbillDataSource osgiKillbillDataSource) {
+        super(logService, osgiKillbillAPI);
+
+        this.bacDao = new BusinessAccountDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
+        this.bstDao = new BusinessSubscriptionTransitionDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
+        this.binDao = new BusinessInvoiceDao(logService, osgiKillbillAPI, osgiKillbillDataSource, bacDao);
+        this.bipDao = new BusinessInvoicePaymentDao(logService, osgiKillbillAPI, osgiKillbillDataSource, bacDao, binDao);
+        this.bosDao = new BusinessOverdueStatusDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
+
+        this.locker = new MySqlGlobalLocker(osgiKillbillDataSource.getDataSource());
     }
 
     @Override
     public void handleKillbillEvent(final ExtBusEvent killbillEvent) {
-        // TODO
-    }
+        final CallContext callContext = new AnalyticsCallContext(killbillEvent);
 
-    private void handleEffectiveSubscriptionTransitionChange(final EffectiveSubscriptionInternalEvent eventEffective) {
-        updateWithAccountLock(eventEffective.getAccountRecordId(), new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                // The event is used as a trigger to rebuild all transitions for this bundle
-                bstDao.rebuildTransitionsForBundle(eventEffective.getBundleId(), createCallContext(eventEffective));
-                return null;
+        try {
+            switch (killbillEvent.getEventType()) {
+                case ACCOUNT_CREATION:
+                case ACCOUNT_CHANGE:
+                    handleAccountEvent(killbillEvent, callContext);
+                    break;
+                case SUBSCRIPTION_CREATION:
+                case SUBSCRIPTION_CHANGE:
+                case SUBSCRIPTION_CANCEL:
+                    handleSubscriptionEvent(killbillEvent, callContext);
+                    break;
+                case OVERDUE_CHANGE:
+                    handleOverdueEvent(killbillEvent, callContext);
+                    break;
+                case INVOICE_CREATION:
+                    handleInvoiceEvent(killbillEvent, callContext);
+                    break;
+                case PAYMENT_SUCCESS:
+                case PAYMENT_FAILED:
+                    handlePaymentEvent(killbillEvent, callContext);
+                    break;
+                default:
+                    // TODO invoice adjustments
+                    // TODO refunds
+                    // TODO tags and custom fields
+                    break;
             }
-        });
+        } catch (AnalyticsRefreshException e) {
+            logService.log(LogService.LOG_WARNING, "Refresh triggered by event " + killbillEvent + " failed", e);
+        }
     }
 
-    private void handleRequestedSubscriptionTransitionChange(final RequestedSubscriptionInternalEvent eventRequested) {
-        updateWithAccountLock(eventRequested.getAccountRecordId(), new Callable<Void>() {
+    private void handleAccountEvent(final ExtBusEvent killbillEvent, final CallContext callContext) throws AnalyticsRefreshException {
+        updateWithAccountLock(killbillEvent, new Callable<Void>() {
             @Override
             public Void call() throws Exception {
-                // The event is used as a trigger to rebuild all transitions for this bundle
-                bstDao.rebuildTransitionsForBundle(eventRequested.getBundleId(), createCallContext(eventRequested));
+                bacDao.update(killbillEvent.getAccountId(), callContext);
                 return null;
             }
         });
     }
 
-    private void handleRepairEntitlement(final RepairEntitlementInternalEvent event) {
-        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
+    private void handleSubscriptionEvent(final ExtBusEvent killbillEvent, final CallContext callContext) throws AnalyticsRefreshException {
+        final UUID bundleId = getBundleIdFromEvent(killbillEvent, callContext);
+        updateWithAccountLock(killbillEvent, new Callable<Void>() {
             @Override
             public Void call() throws Exception {
-                // In case of repair, just rebuild all transitions
-                bstDao.rebuildTransitionsForBundle(event.getBundleId(), createCallContext(event));
+                bstDao.update(bundleId, callContext);
                 return null;
             }
         });
     }
 
-    private void handleAccountCreation(final AccountCreationInternalEvent event) {
-        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
+    private void handleInvoiceEvent(final ExtBusEvent killbillEvent, final CallContext callContext) throws AnalyticsRefreshException {
+        updateWithAccountLock(killbillEvent, new Callable<Void>() {
             @Override
             public Void call() throws Exception {
-                bacDao.accountUpdated(event.getId(), createCallContext(event));
+                binDao.update(killbillEvent.getAccountId(), callContext);
                 return null;
             }
         });
     }
 
-    private void handleAccountChange(final AccountChangeInternalEvent event) {
-        if (!event.hasChanges()) {
-            return;
-        }
-
-        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
+    private void handlePaymentEvent(final ExtBusEvent killbillEvent, final CallContext callContext) throws AnalyticsRefreshException {
+        final UUID paymentId = getPaymentIdFromEvent(killbillEvent, callContext);
+        updateWithAccountLock(killbillEvent, new Callable<Void>() {
             @Override
             public Void call() throws Exception {
-                bacDao.accountUpdated(event.getAccountId(), createCallContext(event));
+                bipDao.update(killbillEvent.getAccountId(), paymentId, callContext);
                 return null;
             }
         });
     }
 
-    private void handleInvoiceCreation(final InvoiceCreationInternalEvent event) {
-        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
+    private void handleOverdueEvent(final ExtBusEvent killbillEvent, final CallContext callContext) throws AnalyticsRefreshException {
+        updateWithAccountLock(killbillEvent, new Callable<Void>() {
             @Override
             public Void call() throws Exception {
-                // The event is used as a trigger to rebuild all invoices and invoice items for this account
-                invoiceDao.rebuildInvoicesForAccount(event.getAccountId(), createCallContext(event));
+                bosDao.update(killbillEvent.getObjectType(), killbillEvent.getObjectId(), callContext);
                 return null;
             }
         });
     }
 
-    private void handleNullInvoice(final NullInvoiceInternalEvent event) {
-        // Ignored for now
-    }
-
-    private void handleInvoiceAdjustment(final InvoiceAdjustmentInternalEvent event) {
-        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                // The event is used as a trigger to rebuild all invoices and invoice items for this account
-                invoiceDao.rebuildInvoicesForAccount(event.getAccountId(), createCallContext(event));
+    private UUID getBundleIdFromEvent(final ExtBusEvent killbillEvent, final TenantContext tenantContext) throws AnalyticsRefreshException {
+        switch (killbillEvent.getObjectType()) {
+            case BUNDLE:
+                return killbillEvent.getObjectId();
+            case SUBSCRIPTION:
+                return getSubscription(killbillEvent.getObjectId(), tenantContext).getBundleId();
+            default:
                 return null;
-            }
-        });
+        }
     }
 
-    private void handlePaymentInfo(final PaymentInfoInternalEvent paymentInfo) {
-        updateWithAccountLock(paymentInfo.getAccountRecordId(), new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                bipDao.invoicePaymentPosted(paymentInfo.getAccountId(),
-                                            paymentInfo.getPaymentId(),
-                                            paymentInfo.getStatus().toString(),
-                                            createCallContext(paymentInfo));
+    private UUID getPaymentIdFromEvent(final ExtBusEvent killbillEvent, final TenantContext tenantContext) {
+        switch (killbillEvent.getObjectType()) {
+            case PAYMENT:
+                return killbillEvent.getObjectId();
+            default:
                 return null;
-            }
-        });
+        }
     }
 
-    private void handlePaymentError(final PaymentErrorInternalEvent paymentError) {
-        updateWithAccountLock(paymentError.getAccountRecordId(), new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                bipDao.invoicePaymentPosted(paymentError.getAccountId(),
-                                            paymentError.getPaymentId(),
-                                            paymentError.getMessage(),
-                                            createCallContext(paymentError));
-                return null;
-            }
-        });
-    }
+    private static final class AnalyticsCallContext implements CallContext {
 
-    private void handleOverdueChange(final OverdueChangeInternalEvent changeEvent) {
-        updateWithAccountLock(changeEvent.getAccountRecordId(), new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                bosDao.overdueStatusChanged(changeEvent.getOverdueObjectType(), changeEvent.getOverdueObjectId(), createCallContext(changeEvent));
-                return null;
-            }
-        });
-    }
+        private static final String USER_NAME = AnalyticsListener.class.getName();
 
-    private void handleControlTagCreation(final ControlTagCreationInternalEvent event) {
-        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                tagDao.tagAdded(event.getObjectType(), event.getObjectId(), event.getTagDefinition().getName(), createCallContext(event));
-                return null;
-            }
-        });
-    }
+        private final ExtBusEvent killbillEvent;
+        private final DateTime now;
 
-    private void handleControlTagDeletion(final ControlTagDeletionInternalEvent event) {
-        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                tagDao.tagRemoved(event.getObjectType(), event.getObjectId(), event.getTagDefinition().getName(), createCallContext(event));
-                return null;
-            }
-        });
-    }
+        private AnalyticsCallContext(final ExtBusEvent killbillEvent) {
+            this.killbillEvent = killbillEvent;
+            this.now = new DateTime(DateTimeZone.UTC);
+        }
 
-    private void handleUserTagCreation(final UserTagCreationInternalEvent event) {
-        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                tagDao.tagAdded(event.getObjectType(), event.getObjectId(), event.getTagDefinition().getName(), createCallContext(event));
-                return null;
-            }
-        });
-    }
+        @Override
+        public UUID getUserToken() {
+            return UUID.randomUUID();
+        }
 
-    private void handleUserTagDeletion(final UserTagDeletionInternalEvent event) {
-        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                tagDao.tagRemoved(event.getObjectType(), event.getObjectId(), event.getTagDefinition().getName(), createCallContext(event));
-                return null;
-            }
-        });
-    }
+        @Override
+        public String getUserName() {
+            return USER_NAME;
+        }
 
-    private void handleControlTagDefinitionCreation(final ControlTagDefinitionCreationInternalEvent event) {
-        // Ignored for now
-    }
+        @Override
+        public CallOrigin getCallOrigin() {
+            return CallOrigin.EXTERNAL;
+        }
 
-    private void handleControlTagDefinitionDeletion(final ControlTagDefinitionDeletionInternalEvent event) {
-        // Ignored for now
-    }
+        @Override
+        public UserType getUserType() {
+            return UserType.SYSTEM;
+        }
 
-    private void handleUserTagDefinitionCreation(final UserTagDefinitionCreationInternalEvent event) {
-        // Ignored for now
-    }
+        @Override
+        public String getReasonCode() {
+            return killbillEvent.getEventType().toString();
+        }
 
-    private void handleUserTagDefinitionDeletion(final UserTagDefinitionDeletionInternalEvent event) {
-        // Ignored for now
-    }
+        @Override
+        public String getComments() {
+            return "eventType=" + killbillEvent.getEventType() + ", objectType="
+                   + killbillEvent.getObjectType() + ", objectId=" + killbillEvent.getObjectId() + ", accountId="
+                   + killbillEvent.getAccountId() + ", tenantId=" + killbillEvent.getTenantId();
+        }
 
-    private InternalCallContext createCallContext(final BusInternalEvent event) {
-        return internalCallContextFactory.createInternalCallContext(event.getTenantRecordId(), event.getAccountRecordId(),
-                                                                    "AnalyticsService", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
+        @Override
+        public DateTime getCreatedDate() {
+            return now;
+        }
+
+        @Override
+        public DateTime getUpdatedDate() {
+            return now;
+        }
+
+        @Override
+        public UUID getTenantId() {
+            return killbillEvent.getTenantId();
+        }
     }
 
-    private <T> T updateWithAccountLock(final Long accountRecordId, final Callable<T> task) {
+    private <T> T updateWithAccountLock(final ExtBusEvent killbillEvent, final Callable<T> task) {
         GlobalLock lock = null;
         try {
-            final String lockKey = accountRecordId == null ? "0" : accountRecordId.toString();
-            lock = locker.lockWithNumberOfTries(LockerType.ACCOUNT_FOR_ANALYTICS, lockKey, NB_LOCK_TRY);
+            final String lockKey = killbillEvent.getAccountId() == null ? "0" : killbillEvent.getAccountId().toString();
+            lock = locker.lockWithNumberOfTries("ACCOUNT_FOR_ANALYTICS", lockKey, NB_LOCK_TRY);
             return task.call();
         } catch (Exception e) {
-            log.warn("Exception while refreshing analytics tables", e);
+            logService.log(LogService.LOG_WARNING, "Exception while refreshing analytics tables", e);
         } finally {
             if (lock != null) {
                 lock.release();
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessAccount.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessAccount.java
new file mode 100644
index 0000000..b9fa7c0
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessAccount.java
@@ -0,0 +1,74 @@
+/*
+ * 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.osgi.bundles.analytics.api;
+
+import java.math.BigDecimal;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+
+import com.ning.billing.osgi.bundles.analytics.model.BusinessAccountModelDao;
+
+public class BusinessAccount extends BusinessEntityBase {
+
+    private final String email;
+    private final Integer firstNameLength;
+    private final String currency;
+    private final Integer billingCycleDayLocal;
+    private final String address1;
+    private final String address2;
+    private final String companyName;
+    private final String city;
+    private final String stateOrProvince;
+    private final String country;
+    private final String postalCode;
+    private final String phone;
+    private final Boolean isMigrated;
+    private final Boolean isNotifiedForInvoices;
+    private final BigDecimal balance;
+    private final LocalDate lastInvoiceDate;
+    private final DateTime lastPaymentDate;
+    private final String lastPaymentStatus;
+
+    public BusinessAccount(final BusinessAccountModelDao businessAccountModelDao) {
+        super(businessAccountModelDao.getCreatedDate(),
+              businessAccountModelDao.getCreatedBy(),
+              businessAccountModelDao.getCreatedReasonCode(),
+              businessAccountModelDao.getCreatedComments(),
+              businessAccountModelDao.getAccountId(),
+              businessAccountModelDao.getAccountName(),
+              businessAccountModelDao.getAccountExternalKey());
+        this.email = businessAccountModelDao.getEmail();
+        this.firstNameLength = businessAccountModelDao.getFirstNameLength();
+        this.currency = businessAccountModelDao.getCurrency();
+        this.billingCycleDayLocal = businessAccountModelDao.getBillingCycleDayLocal();
+        this.address1 = businessAccountModelDao.getAddress1();
+        this.address2 = businessAccountModelDao.getAddress2();
+        this.companyName = businessAccountModelDao.getCompanyName();
+        this.city = businessAccountModelDao.getCity();
+        this.stateOrProvince = businessAccountModelDao.getStateOrProvince();
+        this.country = businessAccountModelDao.getCountry();
+        this.postalCode = businessAccountModelDao.getPostalCode();
+        this.phone = businessAccountModelDao.getPhone();
+        this.isMigrated = businessAccountModelDao.getMigrated();
+        this.isNotifiedForInvoices = businessAccountModelDao.getNotifiedForInvoices();
+        this.balance = businessAccountModelDao.getBalance();
+        this.lastInvoiceDate = businessAccountModelDao.getLastInvoiceDate();
+        this.lastPaymentDate = businessAccountModelDao.getLastPaymentDate();
+        this.lastPaymentStatus = businessAccountModelDao.getLastPaymentStatus();
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessInvoice.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessInvoice.java
new file mode 100644
index 0000000..2a2fd6b
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessInvoice.java
@@ -0,0 +1,67 @@
+/*
+ * 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.osgi.bundles.analytics.api;
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.LocalDate;
+
+import com.ning.billing.osgi.bundles.analytics.model.BusinessInvoiceItemBaseModelDao;
+import com.ning.billing.osgi.bundles.analytics.model.BusinessInvoiceModelDao;
+
+public class BusinessInvoice extends BusinessEntityBase {
+
+    private final UUID invoiceId;
+    private final Integer invoiceNumber;
+    private final LocalDate invoiceDate;
+    private final LocalDate targetDate;
+    private final String currency;
+    private final BigDecimal balance;
+    private final BigDecimal amountPaid;
+    private final BigDecimal amountCharged;
+    private final BigDecimal originalAmountCharged;
+    private final BigDecimal amountCredited;
+    private final List<BusinessInvoiceItem> invoiceItems = new LinkedList<BusinessInvoiceItem>();
+
+    public BusinessInvoice(final BusinessInvoiceModelDao businessInvoiceModelDao,
+                           final Collection<BusinessInvoiceItemBaseModelDao> businessInvoiceItemModelDaos) {
+        super(businessInvoiceModelDao.getCreatedDate(),
+              businessInvoiceModelDao.getCreatedBy(),
+              businessInvoiceModelDao.getCreatedReasonCode(),
+              businessInvoiceModelDao.getCreatedComments(),
+              businessInvoiceModelDao.getAccountId(),
+              businessInvoiceModelDao.getAccountName(),
+              businessInvoiceModelDao.getAccountExternalKey());
+        this.invoiceId = businessInvoiceModelDao.getInvoiceId();
+        this.invoiceNumber = businessInvoiceModelDao.getInvoiceNumber();
+        this.invoiceDate = businessInvoiceModelDao.getInvoiceDate();
+        this.targetDate = businessInvoiceModelDao.getTargetDate();
+        this.currency = businessInvoiceModelDao.getCurrency();
+        this.balance = businessInvoiceModelDao.getBalance();
+        this.amountPaid = businessInvoiceModelDao.getAmountPaid();
+        this.amountCharged = businessInvoiceModelDao.getAmountCharged();
+        this.originalAmountCharged = businessInvoiceModelDao.getOriginalAmountCharged();
+        this.amountCredited = businessInvoiceModelDao.getAmountCredited();
+        for (final BusinessInvoiceItemBaseModelDao businessInvoiceItemModelDao : businessInvoiceItemModelDaos) {
+            invoiceItems.add(new BusinessInvoiceItem(businessInvoiceItemModelDao));
+        }
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessInvoiceItem.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessInvoiceItem.java
new file mode 100644
index 0000000..8b3674c
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessInvoiceItem.java
@@ -0,0 +1,92 @@
+/*
+ * 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.osgi.bundles.analytics.api;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.osgi.bundles.analytics.model.BusinessInvoiceItemBaseModelDao;
+
+public class BusinessInvoiceItem extends BusinessEntityBase {
+
+    private final UUID itemId;
+    private final UUID invoiceId;
+    private final Integer invoiceNumber;
+    private final DateTime invoiceCreatedDate;
+    private final LocalDate invoiceDate;
+    private final LocalDate invoiceTargetDate;
+    private final String invoiceCurrency;
+    private final BigDecimal invoiceBalance;
+    private final BigDecimal invoiceAmountPaid;
+    private final BigDecimal invoiceAmountCharged;
+    private final BigDecimal invoiceOriginalAmountCharged;
+    private final BigDecimal invoiceAmountCredited;
+    private final String itemType;
+    private final Boolean recognizable;
+    private final String bundleExternalKey;
+    private final String productName;
+    private final String productType;
+    private final String productCategory;
+    private final String slug;
+    private final String phase;
+    private final String billingPeriod;
+    private final LocalDate startDate;
+    private final LocalDate endDate;
+    private final BigDecimal amount;
+    private final Currency currency;
+    private final UUID linkedItemId;
+
+    public BusinessInvoiceItem(final BusinessInvoiceItemBaseModelDao businessInvoiceItemBaseModelDao) {
+        super(businessInvoiceItemBaseModelDao.getCreatedDate(),
+              businessInvoiceItemBaseModelDao.getCreatedBy(),
+              businessInvoiceItemBaseModelDao.getCreatedReasonCode(),
+              businessInvoiceItemBaseModelDao.getCreatedComments(),
+              businessInvoiceItemBaseModelDao.getAccountId(),
+              businessInvoiceItemBaseModelDao.getAccountName(),
+              businessInvoiceItemBaseModelDao.getAccountExternalKey());
+        this.itemId = businessInvoiceItemBaseModelDao.getItemId();
+        this.invoiceId = businessInvoiceItemBaseModelDao.getInvoiceId();
+        this.invoiceNumber = businessInvoiceItemBaseModelDao.getInvoiceNumber();
+        this.invoiceCreatedDate = businessInvoiceItemBaseModelDao.getInvoiceCreatedDate();
+        this.invoiceDate = businessInvoiceItemBaseModelDao.getInvoiceDate();
+        this.invoiceTargetDate = businessInvoiceItemBaseModelDao.getInvoiceTargetDate();
+        this.invoiceCurrency = businessInvoiceItemBaseModelDao.getInvoiceCurrency();
+        this.invoiceBalance = businessInvoiceItemBaseModelDao.getInvoiceBalance();
+        this.invoiceAmountPaid = businessInvoiceItemBaseModelDao.getInvoiceAmountPaid();
+        this.invoiceAmountCharged = businessInvoiceItemBaseModelDao.getInvoiceAmountCharged();
+        this.invoiceOriginalAmountCharged = businessInvoiceItemBaseModelDao.getInvoiceOriginalAmountCharged();
+        this.invoiceAmountCredited = businessInvoiceItemBaseModelDao.getInvoiceAmountCredited();
+        this.itemType = businessInvoiceItemBaseModelDao.getItemType();
+        this.recognizable = businessInvoiceItemBaseModelDao.getRecognizable();
+        this.bundleExternalKey = businessInvoiceItemBaseModelDao.getBundleExternalKey();
+        this.productName = businessInvoiceItemBaseModelDao.getProductName();
+        this.productType = businessInvoiceItemBaseModelDao.getProductType();
+        this.productCategory = businessInvoiceItemBaseModelDao.getProductCategory();
+        this.slug = businessInvoiceItemBaseModelDao.getSlug();
+        this.phase = businessInvoiceItemBaseModelDao.getPhase();
+        this.billingPeriod = businessInvoiceItemBaseModelDao.getBillingPeriod();
+        this.startDate = businessInvoiceItemBaseModelDao.getStartDate();
+        this.endDate = businessInvoiceItemBaseModelDao.getEndDate();
+        this.amount = businessInvoiceItemBaseModelDao.getAmount();
+        this.currency = businessInvoiceItemBaseModelDao.getCurrency();
+        this.linkedItemId = businessInvoiceItemBaseModelDao.getLinkedItemId();
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessInvoicePayment.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessInvoicePayment.java
new file mode 100644
index 0000000..65d5a29
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessInvoicePayment.java
@@ -0,0 +1,74 @@
+/*
+ * 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.osgi.bundles.analytics.api;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.osgi.bundles.analytics.model.BusinessInvoicePaymentBaseModelDao;
+
+public class BusinessInvoicePayment extends BusinessEntityBase {
+
+    private final UUID invoicePaymentId;
+    private final UUID invoiceId;
+    private final Integer invoiceNumber;
+    private final DateTime invoiceCreatedDate;
+    private final LocalDate invoiceDate;
+    private final LocalDate invoiceTargetDate;
+    private final String invoiceCurrency;
+    private final BigDecimal invoiceBalance;
+    private final BigDecimal invoiceAmountPaid;
+    private final BigDecimal invoiceAmountCharged;
+    private final BigDecimal invoiceOriginalAmountCharged;
+    private final BigDecimal invoiceAmountCredited;
+    private final String invoicePaymentType;
+    private final Long paymentNumber;
+    private final UUID linkedInvoicePaymentId;
+    private final BigDecimal amount;
+    private final Currency currency;
+
+    public BusinessInvoicePayment(final BusinessInvoicePaymentBaseModelDao businessInvoicePaymentBaseModelDao) {
+        super(businessInvoicePaymentBaseModelDao.getCreatedDate(),
+              businessInvoicePaymentBaseModelDao.getCreatedBy(),
+              businessInvoicePaymentBaseModelDao.getCreatedReasonCode(),
+              businessInvoicePaymentBaseModelDao.getCreatedComments(),
+              businessInvoicePaymentBaseModelDao.getAccountId(),
+              businessInvoicePaymentBaseModelDao.getAccountName(),
+              businessInvoicePaymentBaseModelDao.getAccountExternalKey());
+        this.invoicePaymentId = businessInvoicePaymentBaseModelDao.getInvoicePaymentId();
+        this.invoiceId = businessInvoicePaymentBaseModelDao.getInvoiceId();
+        this.invoiceNumber = businessInvoicePaymentBaseModelDao.getInvoiceNumber();
+        this.invoiceCreatedDate = businessInvoicePaymentBaseModelDao.getInvoiceCreatedDate();
+        this.invoiceDate = businessInvoicePaymentBaseModelDao.getInvoiceDate();
+        this.invoiceTargetDate = businessInvoicePaymentBaseModelDao.getInvoiceTargetDate();
+        this.invoiceCurrency = businessInvoicePaymentBaseModelDao.getInvoiceCurrency();
+        this.invoiceBalance = businessInvoicePaymentBaseModelDao.getInvoiceBalance();
+        this.invoiceAmountPaid = businessInvoicePaymentBaseModelDao.getInvoiceAmountPaid();
+        this.invoiceAmountCharged = businessInvoicePaymentBaseModelDao.getInvoiceAmountCharged();
+        this.invoiceOriginalAmountCharged = businessInvoicePaymentBaseModelDao.getInvoiceOriginalAmountCharged();
+        this.invoiceAmountCredited = businessInvoicePaymentBaseModelDao.getInvoiceAmountCredited();
+        this.invoicePaymentType = businessInvoicePaymentBaseModelDao.getInvoicePaymentType();
+        this.paymentNumber = businessInvoicePaymentBaseModelDao.getPaymentNumber();
+        this.linkedInvoicePaymentId = businessInvoicePaymentBaseModelDao.getLinkedInvoicePaymentId();
+        this.amount = businessInvoicePaymentBaseModelDao.getAmount();
+        this.currency = businessInvoicePaymentBaseModelDao.getCurrency();
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessAccountDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessAccountDao.java
index 0bce215..2669f64 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessAccountDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessAccountDao.java
@@ -18,137 +18,78 @@ package com.ning.billing.osgi.bundles.analytics;
 
 import java.math.BigDecimal;
 import java.util.Collection;
-import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.joda.time.LocalDate;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.ning.billing.account.api.Account;
-import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessAccountSqlDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessAnalyticsSqlDao;
 import com.ning.billing.osgi.bundles.analytics.model.BusinessAccountModelDao;
 import com.ning.billing.payment.api.Payment;
-import com.ning.billing.payment.api.PaymentApiException;
-import com.ning.billing.payment.api.PaymentMethod;
-import com.ning.billing.util.callcontext.InternalCallContext;
-import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.svcapi.account.AccountInternalApi;
-import com.ning.billing.util.svcapi.invoice.InvoiceInternalApi;
-import com.ning.billing.util.svcapi.payment.PaymentInternalApi;
-
-import com.google.inject.Inject;
-
-public class BusinessAccountDao {
-
-    private static final Logger log = LoggerFactory.getLogger(BusinessAccountDao.class);
-
-    private final BusinessAccountSqlDao sqlDao;
-    private final AccountInternalApi accountApi;
-    private final InvoiceInternalApi invoiceApi;
-    private final PaymentInternalApi paymentApi;
-
-    @Inject
-    public BusinessAccountDao(final BusinessAccountSqlDao sqlDao, final AccountInternalApi accountApi,
-                              final InvoiceInternalApi invoiceApi, final PaymentInternalApi paymentApi) {
-        this.sqlDao = sqlDao;
-        this.accountApi = accountApi;
-        this.invoiceApi = invoiceApi;
-        this.paymentApi = paymentApi;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.callcontext.CallContext;
+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 BusinessAccountDao extends BusinessAnalyticsDaoBase {
+
+    public BusinessAccountDao(final OSGIKillbillLogService logService,
+                              final OSGIKillbillAPI osgiKillbillAPI,
+                              final OSGIKillbillDataSource osgiKillbillDataSource) {
+        super(logService, osgiKillbillAPI, osgiKillbillDataSource);
     }
 
-    public void accountUpdated(final UUID accountId, final InternalCallContext context) {
-        final Account account;
-        try {
-            account = accountApi.getAccountById(accountId, context);
-        } catch (AccountApiException e) {
-            log.warn("Error encountered creating BusinessAccountModelDao", e);
-            return;
-        }
+    public void update(final UUID accountId, final CallContext context) throws AnalyticsRefreshException {
+        final Account account = getAccount(accountId, context);
 
-        final BusinessAccountModelDao bac = createBusinessAccountFromAccount(account, context);
-        sqlDao.inTransaction(new Transaction<Void, BusinessAccountSqlDao>() {
+        // Recompute the account record
+        final BusinessAccountModelDao bac = createBusinessAccount(account, context);
+
+        sqlDao.inTransaction(new Transaction<Void, BusinessAnalyticsSqlDao>() {
             @Override
-            public Void inTransaction(final BusinessAccountSqlDao transactional, final TransactionStatus status) throws Exception {
-                updateAccountInTransaction(bac, transactional, context);
+            public Void inTransaction(final BusinessAnalyticsSqlDao transactional, final TransactionStatus status) throws Exception {
+                updateInTransaction(bac, transactional, context);
                 return null;
             }
         });
     }
 
-    // Called also from BusinessInvoiceDao and BusinessInvoicePaymentDao.
     // Note: computing the BusinessAccountModelDao object is fairly expensive, hence should be done outside of the transaction
-    public void updateAccountInTransaction(final BusinessAccountModelDao bac, final BusinessAccountSqlDao transactional, final InternalCallContext context) {
-        log.info("ACCOUNT UPDATE " + bac);
-        transactional.deleteAccount(bac.getAccountId().toString(), context);
-        // Note! There is a window of doom here since we use read committed transactional level by default
-        transactional.createAccount(bac, context);
+    public void updateInTransaction(final BusinessAccountModelDao bac, final BusinessAnalyticsSqlDao transactional, final CallContext context) {
+        transactional.deleteByAccountRecordId(bac.getTableName(), bac.getAccountRecordId(), bac.getTenantRecordId(), context);
+        transactional.create(bac.getTableName(), bac, context);
     }
 
-    public BusinessAccountModelDao createBusinessAccountFromAccount(final Account account, final InternalTenantContext context) {
-        final BusinessAccountModelDao bac = new BusinessAccountModelDao(account);
-
-        try {
-            LocalDate lastInvoiceDate = bac.getLastInvoiceDate();
-            BigDecimal totalInvoiceBalance = bac.getTotalInvoiceBalance();
-            String lastPaymentStatus = bac.getLastPaymentStatus();
-            String paymentMethodType = bac.getPaymentMethod();
-            String creditCardType = bac.getCreditCardType();
-            String billingAddressCountry = bac.getBillingAddressCountry();
-
-            // Retrieve invoices information
-            final Collection<Invoice> invoices = invoiceApi.getInvoicesByAccountId(account.getId(), context);
-            if (invoices != null && invoices.size() > 0) {
-                for (final Invoice invoice : invoices) {
-                    totalInvoiceBalance = totalInvoiceBalance.add(invoice.getBalance());
-
-                    if (lastInvoiceDate == null || invoice.getInvoiceDate().isAfter(lastInvoiceDate)) {
-                        lastInvoiceDate = invoice.getInvoiceDate();
-                    }
-                }
-
-                // Retrieve payments information for these invoices
-                DateTime lastPaymentDate = null;
-
-                final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), context);
-                if (payments != null) {
-                    for (final Payment cur : payments) {
-                        // Use the last payment method/type/country as the default one for the account
-                        if (lastPaymentDate == null || cur.getEffectiveDate().isAfter(lastPaymentDate)) {
-                            lastPaymentDate = cur.getEffectiveDate();
-                            lastPaymentStatus = cur.getPaymentStatus().toString();
-                        }
-                    }
-                }
+    public BusinessAccountModelDao createBusinessAccount(final Account account, final CallContext context) throws AnalyticsRefreshException {
+        // Retrieve the account creation audit log
+        final AuditLog creationAuditLog = getAccountCreationAuditLog(account.getId(), context);
+
+        // Retrieve the account balance
+        // Note: since we retrieve the invoices below, we could compute it ourselves and avoid fetching the invoices
+        // twice, but that way the computation logic is owned by invoice
+        final BigDecimal accountBalance = getAccountBalance(account.getId(), context);
+
+        // Retrieve invoices information
+        Invoice lastInvoice = null;
+        final Collection<Invoice> invoices = getInvoicesByAccountId(account.getId(), context);
+        for (final Invoice invoice : invoices) {
+            if (lastInvoice == null || invoice.getInvoiceDate().isAfter(lastInvoice.getInvoiceDate())) {
+                lastInvoice = invoice;
             }
+        }
 
-            // Retrieve payment methods
-            for (final PaymentMethod paymentMethod : paymentApi.getPaymentMethods(account, context)) {
-                if (paymentMethod.getId().equals(account.getPaymentMethodId()) && paymentMethod.getPluginDetail() != null) {
-                    paymentMethodType = PaymentMethodUtils.getPaymentMethodType(paymentMethod.getPluginDetail());
-                    creditCardType = PaymentMethodUtils.getCardType(paymentMethod.getPluginDetail());
-                    billingAddressCountry = PaymentMethodUtils.getCardCountry(paymentMethod.getPluginDetail());
-                    break;
-                }
+        // Retrieve payments information
+        Payment lastPayment = null;
+        final Collection<Payment> payments = getPaymentsByAccountId(account.getId(), context);
+        for (final Payment payment : payments) {
+            if (lastPayment == null || payment.getEffectiveDate().isAfter(lastPayment.getEffectiveDate())) {
+                lastPayment = payment;
             }
-
-            bac.setLastPaymentStatus(lastPaymentStatus);
-            bac.setPaymentMethod(paymentMethodType);
-            bac.setCreditCardType(creditCardType);
-            bac.setBillingAddressCountry(billingAddressCountry);
-            bac.setLastInvoiceDate(lastInvoiceDate);
-            bac.setTotalInvoiceBalance(totalInvoiceBalance);
-
-            bac.setBalance(invoiceApi.getAccountBalance(account.getId(), context));
-        } catch (PaymentApiException ex) {
-            log.error(String.format("Failed to handle account update for account %s", account.getId()), ex);
         }
 
-        return bac;
+        return new BusinessAccountModelDao(account, accountBalance, lastInvoice, lastPayment, creationAuditLog);
     }
 }
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
new file mode 100644
index 0000000..20a0d02
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessAnalyticsBase.java
@@ -0,0 +1,340 @@
+/*
+ * 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.osgi.bundles.analytics;
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.osgi.service.log.LogService;
+
+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.AccountUserApi;
+import com.ning.billing.catalog.api.Catalog;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.CatalogUserApi;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+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.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.payment.api.Payment;
+import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentApiException;
+import com.ning.billing.payment.api.PaymentMethod;
+import com.ning.billing.util.api.AuditLevel;
+import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.audit.AuditLogsForAccount;
+import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillAPI;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
+
+public abstract class BusinessAnalyticsBase {
+
+    protected final OSGIKillbillLogService logService;
+    protected final OSGIKillbillAPI osgiKillbillAPI;
+
+    public BusinessAnalyticsBase(final OSGIKillbillLogService logService, final OSGIKillbillAPI osgiKillbillAPI) {
+        this.logService = logService;
+        this.osgiKillbillAPI = osgiKillbillAPI;
+    }
+
+    //
+    // ACCOUNT
+    //
+
+    protected Account getAccount(final UUID accountId, final TenantContext context) throws AnalyticsRefreshException {
+        final AccountUserApi accountUserApi = getAccountUserApi();
+
+        try {
+            return accountUserApi.getAccountById(accountId, context);
+        } catch (AccountApiException e) {
+            logService.log(LogService.LOG_WARNING, "Error retrieving account for id " + accountId, e);
+            throw new AnalyticsRefreshException(e);
+        }
+    }
+
+    protected AuditLog getAccountCreationAuditLog(final UUID accountId, final TenantContext context) throws AnalyticsRefreshException {
+        final AuditLogsForAccount auditLogsForAccount = getAuditUserApi().getAuditLogsForAccount(accountId, AuditLevel.MINIMAL, context);
+        for (final AuditLog auditLog : auditLogsForAccount.getAccountAuditLogs()) {
+            if (auditLog.getChangeType().equals(ChangeType.INSERT)) {
+                return auditLog;
+            }
+        }
+
+        throw new AnalyticsRefreshException("Unable to find Account creation audit log for id " + accountId);
+    }
+
+    //
+    // SUBSCRIPTION
+    //
+
+    protected SubscriptionBundle getSubscriptionBundle(final UUID bundleId, final TenantContext context) throws AnalyticsRefreshException {
+        final EntitlementUserApi entitlementUserApi = getEntitlementUserApi();
+
+        try {
+            return entitlementUserApi.getBundleFromId(bundleId, context);
+        } catch (EntitlementUserApiException e) {
+            logService.log(LogService.LOG_WARNING, "Error retrieving bundle for id " + bundleId, e);
+            throw new AnalyticsRefreshException(e);
+        }
+    }
+
+    protected Collection<Subscription> getSubscriptionsForBundle(final UUID bundleId, final TenantContext context) throws AnalyticsRefreshException {
+        final EntitlementUserApi entitlementUserApi = getEntitlementUserApi();
+        return entitlementUserApi.getSubscriptionsForBundle(bundleId, context);
+    }
+
+    protected Subscription getSubscription(final UUID subscriptionId, final TenantContext context) throws AnalyticsRefreshException {
+        final EntitlementUserApi entitlementUserApi = getEntitlementUserApi();
+
+        try {
+            return entitlementUserApi.getSubscriptionFromId(subscriptionId, context);
+        } catch (EntitlementUserApiException e) {
+            logService.log(LogService.LOG_WARNING, "Error retrieving subscription for id " + subscriptionId, e);
+            throw new AnalyticsRefreshException(e);
+        }
+    }
+
+    //
+    // INVOICE
+    //
+
+    protected Invoice getInvoice(final UUID invoiceId, final TenantContext context) throws AnalyticsRefreshException {
+        final InvoiceUserApi invoiceUserApi = getInvoiceUserApi();
+        try {
+            return invoiceUserApi.getInvoice(invoiceId, context);
+        } catch (InvoiceApiException e) {
+            logService.log(LogService.LOG_WARNING, "Error retrieving subscription for id " + invoiceId, e);
+            throw new AnalyticsRefreshException(e);
+        }
+    }
+
+    protected AuditLog getInvoiceCreationAuditLog(final UUID invoiceId, final TenantContext context) throws AnalyticsRefreshException {
+        final List<AuditLog> auditLogsForInvoice = getAuditUserApi().getAuditLogs(invoiceId, ObjectType.INVOICE, AuditLevel.MINIMAL, context);
+        for (final AuditLog auditLog : auditLogsForInvoice) {
+            if (auditLog.getChangeType().equals(ChangeType.INSERT)) {
+                return auditLog;
+            }
+        }
+
+        throw new AnalyticsRefreshException("Unable to find Invoice creation audit log for id " + invoiceId);
+    }
+
+    protected AuditLog getInvoiceItemCreationAuditLog(final UUID invoiceItemId, final TenantContext context) throws AnalyticsRefreshException {
+        final List<AuditLog> auditLogsForInvoiceItem = getAuditUserApi().getAuditLogs(invoiceItemId, ObjectType.INVOICE_ITEM, AuditLevel.MINIMAL, context);
+        for (final AuditLog auditLog : auditLogsForInvoiceItem) {
+            if (auditLog.getChangeType().equals(ChangeType.INSERT)) {
+                return auditLog;
+            }
+        }
+
+        throw new AnalyticsRefreshException("Unable to find Invoice item creation audit log for id " + invoiceItemId);
+    }
+
+    protected Collection<Invoice> getInvoicesByAccountId(final UUID accountId, final CallContext context) throws AnalyticsRefreshException {
+        final InvoiceUserApi invoiceUserApi = getInvoiceUserApi();
+        return invoiceUserApi.getInvoicesByAccount(accountId, context);
+    }
+
+    protected BigDecimal getAccountBalance(final UUID accountId, final CallContext context) throws AnalyticsRefreshException {
+        final InvoiceUserApi invoiceUserApi = getInvoiceUserApi();
+        return invoiceUserApi.getAccountBalance(accountId, context);
+    }
+
+    protected Plan getPlanFromInvoiceItem(final InvoiceItem invoiceItem, final TenantContext context) throws AnalyticsRefreshException {
+        try {
+            final Catalog catalog = getCatalog(context);
+            return catalog.findPlan(invoiceItem.getPlanName(), invoiceItem.getStartDate().toDateTimeAtStartOfDay());
+        } catch (CatalogApiException e) {
+            logService.log(LogService.LOG_WARNING, "Unable to retrieve plan for invoice item " + invoiceItem.getId(), e);
+            throw new AnalyticsRefreshException(e);
+        }
+    }
+
+    protected PlanPhase getPlanPhaseFromInvoiceItem(final InvoiceItem invoiceItem, final TenantContext context) throws AnalyticsRefreshException {
+        final Subscription subscription = getSubscription(invoiceItem.getSubscriptionId(), context);
+
+        try {
+            final Catalog catalog = getCatalog(context);
+            return catalog.findPhase(invoiceItem.getPhaseName(), invoiceItem.getStartDate().toDateTimeAtStartOfDay(), subscription.getStartDate());
+        } catch (CatalogApiException e) {
+            logService.log(LogService.LOG_WARNING, "Unable to retrieve phase for invoice item " + invoiceItem.getId(), e);
+            throw new AnalyticsRefreshException(e);
+        }
+    }
+
+    //
+    // CATALOG
+    //
+
+    protected Catalog getCatalog(final TenantContext context) throws AnalyticsRefreshException {
+        final CatalogUserApi catalogUserApi = getCatalogUserApi();
+
+        // TODO FIXME
+        final String catalogName = "Please, please, fix me!!!";
+        return catalogUserApi.getCatalog(catalogName, context);
+    }
+
+    //
+    // INVOICE PAYMENT
+    //
+
+    protected Collection<InvoicePayment> getAccountInvoicePayments(final UUID accountId, final TenantContext context) throws AnalyticsRefreshException {
+        final InvoicePaymentApi invoicePaymentApi = getInvoicePaymentUserApi();
+        final Collection<Payment> payments = getPaymentsByAccountId(accountId, context);
+
+        final Collection<InvoicePayment> invoicePayments = new LinkedList<InvoicePayment>();
+        for (final Payment payment : payments) {
+            invoicePayments.addAll(invoicePaymentApi.getInvoicePayments(payment.getId(), context));
+        }
+        return invoicePayments;
+    }
+
+    protected AuditLog getInvoicePaymentCreationAuditLog(final UUID invoicePaymentId, final TenantContext context) throws AnalyticsRefreshException {
+        final List<AuditLog> auditLogsForInvoicePayment = getAuditUserApi().getAuditLogs(invoicePaymentId, ObjectType.INVOICE_PAYMENT, AuditLevel.MINIMAL, context);
+        for (final AuditLog auditLog : auditLogsForInvoicePayment) {
+            if (auditLog.getChangeType().equals(ChangeType.INSERT)) {
+                return auditLog;
+            }
+        }
+
+        throw new AnalyticsRefreshException("Unable to find Invoice payment creation audit log for id " + invoicePaymentId);
+    }
+
+    //
+    // PAYMENT
+    //
+
+    protected Collection<Payment> getPaymentsByAccountId(final UUID accountId, final TenantContext context) throws AnalyticsRefreshException {
+        final PaymentApi paymentApi = getPaymentUserApi();
+        try {
+            return paymentApi.getAccountPayments(accountId, context);
+        } catch (PaymentApiException e) {
+            logService.log(LogService.LOG_WARNING, "Error retrieving payments for account id " + accountId, e);
+            throw new AnalyticsRefreshException(e);
+        }
+    }
+
+    protected Payment getPayment(final UUID paymentId, final TenantContext context) throws AnalyticsRefreshException {
+        final PaymentApi paymentApi = getPaymentUserApi();
+
+        try {
+            return paymentApi.getPayment(paymentId, context);
+        } catch (PaymentApiException e) {
+            logService.log(LogService.LOG_WARNING, "Error retrieving payment for id " + paymentId, e);
+            throw new AnalyticsRefreshException(e);
+        }
+    }
+
+    protected Collection<PaymentMethod> getAccountPaymentMethods(final UUID accountId, final TenantContext context) throws AnalyticsRefreshException {
+        final Account account = getAccount(accountId, context);
+
+        final PaymentApi paymentApi = getPaymentUserApi();
+        try {
+            return paymentApi.getPaymentMethods(account, true, context);
+        } catch (PaymentApiException e) {
+            logService.log(LogService.LOG_WARNING, "Error retrieving payment methods for account id " + accountId, e);
+            throw new AnalyticsRefreshException(e);
+        }
+    }
+
+    protected PaymentMethod getPaymentMethod(final UUID paymentMethodId, final TenantContext context) throws AnalyticsRefreshException {
+        final PaymentApi paymentApi = getPaymentUserApi();
+
+        try {
+            return paymentApi.getPaymentMethodById(paymentMethodId, true, context);
+        } catch (PaymentApiException e) {
+            logService.log(LogService.LOG_WARNING, "Error retrieving payment method for id " + paymentMethodId, e);
+            throw new AnalyticsRefreshException(e);
+        }
+    }
+
+    //
+    // APIs
+    //
+
+    private AccountUserApi getAccountUserApi() throws AnalyticsRefreshException {
+        final AccountUserApi accountUserApi = osgiKillbillAPI.getAccountUserApi();
+        if (accountUserApi == null) {
+            throw new AnalyticsRefreshException("Error retrieving accountUserApi");
+        }
+        return accountUserApi;
+    }
+
+    private AuditUserApi getAuditUserApi() throws AnalyticsRefreshException {
+        final AuditUserApi auditUserApi = osgiKillbillAPI.getAuditUserApi();
+        if (auditUserApi == null) {
+            throw new AnalyticsRefreshException("Error retrieving auditUserApi");
+        }
+        return auditUserApi;
+    }
+
+    private EntitlementUserApi getEntitlementUserApi() throws AnalyticsRefreshException {
+        final EntitlementUserApi entitlementUserApi = osgiKillbillAPI.getEntitlementUserApi();
+        if (entitlementUserApi == null) {
+            throw new AnalyticsRefreshException("Error retrieving entitlementUserApi");
+        }
+        return entitlementUserApi;
+    }
+
+    private InvoiceUserApi getInvoiceUserApi() throws AnalyticsRefreshException {
+        final InvoiceUserApi invoiceUserApi = osgiKillbillAPI.getInvoiceUserApi();
+        if (invoiceUserApi == null) {
+            throw new AnalyticsRefreshException("Error retrieving invoiceUserApi");
+        }
+        return invoiceUserApi;
+    }
+
+    private CatalogUserApi getCatalogUserApi() throws AnalyticsRefreshException {
+        final CatalogUserApi catalogUserApi = osgiKillbillAPI.getCatalogUserApi();
+        if (catalogUserApi == null) {
+            throw new AnalyticsRefreshException("Error retrieving catalogUserApi");
+        }
+        return catalogUserApi;
+    }
+
+    private PaymentApi getPaymentUserApi() throws AnalyticsRefreshException {
+        final PaymentApi paymentApi = osgiKillbillAPI.getPaymentApi();
+        if (paymentApi == null) {
+            throw new AnalyticsRefreshException("Error retrieving paymentApi");
+        }
+        return paymentApi;
+    }
+
+    private InvoicePaymentApi getInvoicePaymentUserApi() throws AnalyticsRefreshException {
+        final InvoicePaymentApi invoicePaymentApi = osgiKillbillAPI.getInvoicePaymentApi();
+        if (invoicePaymentApi == null) {
+            throw new AnalyticsRefreshException("Error retrieving invoicePaymentApi");
+        }
+        return invoicePaymentApi;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessInvoiceDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessInvoiceDao.java
index efbb85e..1d9bee7 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessInvoiceDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessInvoiceDao.java
@@ -23,85 +23,83 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
-import javax.inject.Inject;
-
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.ning.billing.account.api.Account;
-import com.ning.billing.account.api.AccountApiException;
-import com.ning.billing.catalog.api.CatalogApiException;
-import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
-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.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessAccountSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceItemSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceSqlDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessAnalyticsSqlDao;
 import com.ning.billing.osgi.bundles.analytics.model.BusinessAccountModelDao;
-import com.ning.billing.osgi.bundles.analytics.model.BusinessInvoiceItemModelDao;
+import com.ning.billing.osgi.bundles.analytics.model.BusinessInvoiceItemBaseModelDao;
 import com.ning.billing.osgi.bundles.analytics.model.BusinessInvoiceModelDao;
-import com.ning.billing.util.callcontext.InternalCallContext;
-import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.svcapi.account.AccountInternalApi;
-import com.ning.billing.util.svcapi.entitlement.EntitlementInternalApi;
-import com.ning.billing.util.svcapi.invoice.InvoiceInternalApi;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillAPI;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillDataSource;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
 
 import com.google.common.annotations.VisibleForTesting;
 
-public class BusinessInvoiceDao {
-
-    private static final Logger log = LoggerFactory.getLogger(BusinessInvoiceDao.class);
+public class BusinessInvoiceDao extends BusinessAnalyticsDaoBase {
 
-    private final AccountInternalApi accountApi;
-    private final EntitlementInternalApi entitlementApi;
-    private final InvoiceInternalApi invoiceApi;
     private final BusinessAccountDao businessAccountDao;
-    private final BusinessInvoiceSqlDao sqlDao;
-    private final CatalogService catalogService;
-
-    @Inject
-    public BusinessInvoiceDao(final AccountInternalApi accountApi,
-                              final EntitlementInternalApi entitlementApi,
-                              final InvoiceInternalApi invoiceApi,
-                              final BusinessAccountDao businessAccountDao,
-                              final BusinessInvoiceSqlDao sqlDao,
-                              final CatalogService catalogService) {
-        this.accountApi = accountApi;
-        this.entitlementApi = entitlementApi;
-        this.invoiceApi = invoiceApi;
+
+    public BusinessInvoiceDao(final OSGIKillbillLogService logService,
+                              final OSGIKillbillAPI osgiKillbillAPI,
+                              final OSGIKillbillDataSource osgiKillbillDataSource,
+                              final BusinessAccountDao businessAccountDao) {
+        super(logService, osgiKillbillAPI, osgiKillbillDataSource);
         this.businessAccountDao = businessAccountDao;
-        this.sqlDao = sqlDao;
-        this.catalogService = catalogService;
     }
 
-    public void rebuildInvoicesForAccount(final UUID accountId, final InternalCallContext context) {
-        // Lookup the associated account
-        final Account account;
-        try {
-            account = accountApi.getAccountById(accountId, context);
-        } catch (AccountApiException e) {
-            log.warn("Ignoring invoice update for account id {} (account does not exist)", accountId);
-            return;
-        }
+    public void update(final UUID accountId, final CallContext context) throws AnalyticsRefreshException {
+        final Account account = getAccount(accountId, context);
+
+        // Recompute the account record
+        final BusinessAccountModelDao bac = businessAccountDao.createBusinessAccount(account, context);
 
+        // Recompute all invoices and invoice items
+        final Map<BusinessInvoiceModelDao, Collection<BusinessInvoiceItemBaseModelDao>> businessInvoices = createBusinessInvoicesAndInvoiceItems(account, context);
+
+        // Delete and recreate invoice and invoice items in the transaction
+        sqlDao.inTransaction(new Transaction<Void, BusinessAnalyticsSqlDao>() {
+            @Override
+            public Void inTransaction(final BusinessAnalyticsSqlDao transactional, final TransactionStatus status) throws Exception {
+                updateInTransaction(bac, businessInvoices, transactional, context);
+                return null;
+            }
+        });
+    }
+
+    public void updateInTransaction(final BusinessAccountModelDao bac,
+                                    final Map<BusinessInvoiceModelDao, Collection<BusinessInvoiceItemBaseModelDao>> businessInvoices,
+                                    final BusinessAnalyticsSqlDao transactional,
+                                    final CallContext context) throws AnalyticsRefreshException {
+        rebuildInvoicesForAccountInTransaction(bac, businessInvoices, transactional, context);
+
+        // Update invoice and payment details in BAC
+        businessAccountDao.updateInTransaction(bac, transactional, context);
+    }
+
+    public Map<BusinessInvoiceModelDao, Collection<BusinessInvoiceItemBaseModelDao>> createBusinessInvoicesAndInvoiceItems(final Account account,
+                                                                                                                           final CallContext context) throws AnalyticsRefreshException {
         // Lookup the invoices for that account
-        final Collection<Invoice> invoices = invoiceApi.getInvoicesByAccountId(account.getId(), context);
+        final Map<BusinessInvoiceModelDao, Collection<BusinessInvoiceItemBaseModelDao>> businessInvoices = new HashMap<BusinessInvoiceModelDao, Collection<BusinessInvoiceItemBaseModelDao>>();
 
         // Create the business invoice and associated business invoice items
-        final Map<BusinessInvoiceModelDao, Collection<BusinessInvoiceItemModelDao>> businessInvoices = new HashMap<BusinessInvoiceModelDao, Collection<BusinessInvoiceItemModelDao>>();
+        final Collection<Invoice> invoices = getInvoicesByAccountId(account.getId(), context);
         for (final Invoice invoice : invoices) {
-            final BusinessInvoiceModelDao businessInvoice = new BusinessInvoiceModelDao(account.getExternalKey(), invoice);
+            final AuditLog creationAuditLog = getInvoiceCreationAuditLog(invoice.getId(), context);
+            final BusinessInvoiceModelDao businessInvoice = new BusinessInvoiceModelDao(account, invoice, creationAuditLog);
 
-            final List<BusinessInvoiceItemModelDao> businessInvoiceItems = new ArrayList<BusinessInvoiceItemModelDao>();
+            final List<BusinessInvoiceItemBaseModelDao> businessInvoiceItems = new ArrayList<BusinessInvoiceItemBaseModelDao>();
             for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) {
-                final BusinessInvoiceItemModelDao businessInvoiceItem = createBusinessInvoiceItem(invoiceItem, context);
+                final BusinessInvoiceItemBaseModelDao businessInvoiceItem = createBusinessInvoiceItem(account, invoice, invoiceItem, context);
                 if (businessInvoiceItem != null) {
                     businessInvoiceItems.add(businessInvoiceItem);
                 }
@@ -110,112 +108,69 @@ public class BusinessInvoiceDao {
             businessInvoices.put(businessInvoice, businessInvoiceItems);
         }
 
-        // Update the account record
-        final BusinessAccountModelDao bac = businessAccountDao.createBusinessAccountFromAccount(account, context);
-
-        // Delete and recreate invoice and invoice items in the transaction
-        sqlDao.inTransaction(new Transaction<Void, BusinessInvoiceSqlDao>() {
-            @Override
-            public Void inTransaction(final BusinessInvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
-                rebuildInvoicesForAccountInTransaction(account, businessInvoices, transactional, context);
-
-                // Update balance, last invoice date and total invoice balance in BAC
-                final BusinessAccountSqlDao accountSqlDao = transactional.become(BusinessAccountSqlDao.class);
-                businessAccountDao.updateAccountInTransaction(bac, accountSqlDao, context);
-                return null;
-            }
-        });
-    }
-
-    // Used by BIP Recorder
-    public void rebuildInvoiceInTransaction(final String accountKey, final Invoice invoice,
-                                            final BusinessInvoiceSqlDao transactional, final InternalCallContext context) {
-        // Delete the invoice
-        transactional.deleteInvoice(invoice.getId().toString(), context);
-
-        // Re-create it - this will update the various amounts
-        transactional.createInvoice(new BusinessInvoiceModelDao(accountKey, invoice), context);
+        return businessInvoices;
     }
 
-    private void rebuildInvoicesForAccountInTransaction(final Account account, final Map<BusinessInvoiceModelDao, Collection<BusinessInvoiceItemModelDao>> businessInvoices,
-                                                        final BusinessInvoiceSqlDao transactional, final InternalCallContext context) {
-        log.info("Started rebuilding invoices for account id {}", account.getId());
-        deleteInvoicesAndInvoiceItemsForAccountInTransaction(transactional, account.getId(), context);
+    private void rebuildInvoicesForAccountInTransaction(final BusinessAccountModelDao account,
+                                                        final Map<BusinessInvoiceModelDao, Collection<BusinessInvoiceItemBaseModelDao>> businessInvoices,
+                                                        final BusinessAnalyticsSqlDao transactional,
+                                                        final CallContext context) {
+        deleteInvoicesAndInvoiceItemsForAccountInTransaction(transactional, account.getAccountRecordId(), account.getTenantRecordId(), context);
 
         for (final BusinessInvoiceModelDao businessInvoice : businessInvoices.keySet()) {
             createInvoiceInTransaction(transactional, businessInvoice, businessInvoices.get(businessInvoice), context);
         }
-
-        log.info("Finished rebuilding invoices for account id {}", account.getId());
     }
 
-    private void deleteInvoicesAndInvoiceItemsForAccountInTransaction(final BusinessInvoiceSqlDao transactional,
-                                                                      final UUID accountId, final InternalCallContext context) {
-        // We don't use on cascade delete here as we don't want the database layer to be generic - hence we have
-        // to delete the invoice items manually.
-        // Note: invoice items should go first (see query)
-        final BusinessInvoiceItemSqlDao invoiceItemSqlDao = transactional.become(BusinessInvoiceItemSqlDao.class);
-        log.info("Deleting invoice items for account {}", accountId);
-        invoiceItemSqlDao.deleteInvoiceItemsForAccount(accountId.toString(), context);
-
-        log.info("Deleting invoices for account {}", accountId);
-        transactional.deleteInvoicesForAccount(accountId.toString(), context);
+    private void deleteInvoicesAndInvoiceItemsForAccountInTransaction(final BusinessAnalyticsSqlDao transactional,
+                                                                      final Long accountRecordId,
+                                                                      final Long tenantRecordId,
+                                                                      final CallContext context) {
+        // Delete all invoice items
+        for (final String tableName : BusinessInvoiceItemBaseModelDao.ALL_INVOICE_ITEMS_TABLE_NAMES) {
+            transactional.deleteByAccountRecordId(tableName, accountRecordId, tenantRecordId, context);
+        }
+
+        // Delete all invoices
+        transactional.deleteByAccountRecordId(BusinessInvoiceModelDao.INVOICES_TABLE_NAME, accountRecordId, tenantRecordId, context);
     }
 
-    private void createInvoiceInTransaction(final BusinessInvoiceSqlDao transactional, final BusinessInvoiceModelDao invoice,
-                                            final Iterable<BusinessInvoiceItemModelDao> invoiceItems, final InternalCallContext context) {
+    private void createInvoiceInTransaction(final BusinessAnalyticsSqlDao transactional,
+                                            final BusinessInvoiceModelDao invoice,
+                                            final Iterable<BusinessInvoiceItemBaseModelDao> invoiceItems,
+                                            final CallContext context) {
         // Create the invoice
-        log.info("Adding invoice {}", invoice);
-        transactional.createInvoice(invoice, context);
+        transactional.create(invoice.getTableName(), invoice, context);
 
         // Add associated invoice items
-        final BusinessInvoiceItemSqlDao invoiceItemSqlDao = transactional.become(BusinessInvoiceItemSqlDao.class);
-        for (final BusinessInvoiceItemModelDao invoiceItem : invoiceItems) {
-            log.info("Adding invoice item {}", invoiceItem);
-            invoiceItemSqlDao.createInvoiceItem(invoiceItem, context);
+        for (final BusinessInvoiceItemBaseModelDao invoiceItem : invoiceItems) {
+            transactional.create(invoiceItem.getTableName(), invoiceItem, context);
         }
     }
 
     @VisibleForTesting
-    BusinessInvoiceItemModelDao createBusinessInvoiceItem(final InvoiceItem invoiceItem, final InternalTenantContext context) {
-        String externalKey = null;
-        Plan plan = null;
-        PlanPhase planPhase = null;
-
+    BusinessInvoiceItemBaseModelDao createBusinessInvoiceItem(final Account account,
+                                                              final Invoice invoice,
+                                                              final InvoiceItem invoiceItem,
+                                                              final TenantContext context) throws AnalyticsRefreshException {
+        SubscriptionBundle bundle = null;
         // Subscription and bundle could be null for e.g. credits or adjustments
         if (invoiceItem.getBundleId() != null) {
-            try {
-                final SubscriptionBundle bundle = entitlementApi.getBundleFromId(invoiceItem.getBundleId(), context);
-                externalKey = bundle.getExternalKey();
-            } catch (EntitlementUserApiException e) {
-                log.warn("Ignoring subscription fields for invoice item {} for bundle {} (bundle does not exist)",
-                         invoiceItem.getId().toString(),
-                         invoiceItem.getBundleId().toString());
-            }
+            bundle = getSubscriptionBundle(invoiceItem.getBundleId(), context);
         }
 
+        Plan plan = null;
         if (invoiceItem.getPlanName() != null) {
-            try {
-                plan = catalogService.getFullCatalog().findPlan(invoiceItem.getPlanName(), invoiceItem.getStartDate().toDateTimeAtStartOfDay());
-            } catch (CatalogApiException e) {
-                log.warn("Unable to retrieve plan for invoice item {}", invoiceItem.getId());
-            }
+            plan = getPlanFromInvoiceItem(invoiceItem, context);
         }
 
+        PlanPhase planPhase = null;
         if (invoiceItem.getSubscriptionId() != null && invoiceItem.getPhaseName() != null) {
-            final Subscription subscription;
-            try {
-                subscription = entitlementApi.getSubscriptionFromId(invoiceItem.getSubscriptionId(), context);
-                planPhase = catalogService.getFullCatalog().findPhase(invoiceItem.getPhaseName(), invoiceItem.getStartDate().toDateTimeAtStartOfDay(), subscription.getStartDate());
-            } catch (EntitlementUserApiException e) {
-                log.warn("Ignoring subscription fields for invoice item {} for subscription {} (subscription does not exist)",
-                         invoiceItem.getId().toString(),
-                         invoiceItem.getSubscriptionId().toString());
-            } catch (CatalogApiException e) {
-                log.warn("Unable to retrieve phase for invoice item {}", invoiceItem.getId());
-            }
+            planPhase = getPlanPhaseFromInvoiceItem(invoiceItem, context);
         }
 
-        return new BusinessInvoiceItemModelDao(externalKey, invoiceItem, plan, planPhase);
+        final AuditLog creationAuditLog = getInvoiceItemCreationAuditLog(invoiceItem.getId(), context);
+
+        return BusinessInvoiceItemBaseModelDao.create(account, invoice, invoiceItem, bundle, plan, planPhase, creationAuditLog);
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessInvoicePaymentDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessInvoicePaymentDao.java
index b39ad51..6262bfa 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessInvoicePaymentDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessInvoicePaymentDao.java
@@ -16,188 +16,115 @@
 
 package com.ning.billing.osgi.bundles.analytics;
 
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
-import javax.inject.Inject;
 
-import org.joda.time.DateTime;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.ning.billing.account.api.Account;
-import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoicePayment;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessAccountSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoicePaymentSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceSqlDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessAnalyticsSqlDao;
 import com.ning.billing.osgi.bundles.analytics.model.BusinessAccountModelDao;
-import com.ning.billing.osgi.bundles.analytics.model.BusinessInvoicePaymentModelDao;
+import com.ning.billing.osgi.bundles.analytics.model.BusinessInvoiceItemBaseModelDao;
+import com.ning.billing.osgi.bundles.analytics.model.BusinessInvoiceModelDao;
+import com.ning.billing.osgi.bundles.analytics.model.BusinessInvoicePaymentBaseModelDao;
 import com.ning.billing.payment.api.Payment;
-import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.PaymentMethod;
-import com.ning.billing.payment.api.PaymentMethodPlugin;
-import com.ning.billing.util.callcontext.InternalCallContext;
-import com.ning.billing.util.clock.Clock;
-import com.ning.billing.util.svcapi.account.AccountInternalApi;
-import com.ning.billing.util.svcapi.invoice.InvoiceInternalApi;
-import com.ning.billing.util.svcapi.payment.PaymentInternalApi;
-
-public class BusinessInvoicePaymentDao {
-
-    private static final Logger log = LoggerFactory.getLogger(BusinessInvoicePaymentDao.class);
-
-    private final BusinessInvoicePaymentSqlDao invoicePaymentSqlDao;
-    private final AccountInternalApi accountApi;
-    private final InvoiceInternalApi invoiceApi;
-    private final PaymentInternalApi paymentApi;
-    private final Clock clock;
-    private final BusinessInvoiceDao invoiceDao;
-    private final BusinessAccountDao accountDao;
-
-    @Inject
-    public BusinessInvoicePaymentDao(final BusinessInvoicePaymentSqlDao invoicePaymentSqlDao, final AccountInternalApi accountApi,
-                                     final InvoiceInternalApi invoiceApi, final PaymentInternalApi paymentApi,
-                                     final Clock clock, final BusinessInvoiceDao invoiceDao, final BusinessAccountDao accountDao) {
-        this.invoicePaymentSqlDao = invoicePaymentSqlDao;
-        this.accountApi = accountApi;
-        this.invoiceApi = invoiceApi;
-        this.paymentApi = paymentApi;
-        this.clock = clock;
-        this.invoiceDao = invoiceDao;
-        this.accountDao = accountDao;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.callcontext.CallContext;
+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 BusinessInvoicePaymentDao extends BusinessAnalyticsDaoBase {
+
+    private final BusinessAccountDao businessAccountDao;
+    private final BusinessInvoiceDao businessInvoiceDao;
+
+    public BusinessInvoicePaymentDao(final OSGIKillbillLogService logService,
+                                     final OSGIKillbillAPI osgiKillbillAPI,
+                                     final OSGIKillbillDataSource osgiKillbillDataSource,
+                                     final BusinessAccountDao businessAccountDao,
+                                     final BusinessInvoiceDao businessInvoiceDao) {
+        super(logService, osgiKillbillAPI, osgiKillbillDataSource);
+        this.businessAccountDao = businessAccountDao;
+        this.businessInvoiceDao = businessInvoiceDao;
     }
 
-    public void invoicePaymentPosted(final UUID accountId, @Nullable final UUID paymentId, final String message, final InternalCallContext context) {
+    public void update(final UUID accountId, @Nullable final UUID paymentId, final CallContext context) throws AnalyticsRefreshException {
         // Payment attempt with no default payment method. Ignore.
         if (paymentId == null) {
             return;
         }
 
-        final Account account;
-        try {
-            account = accountApi.getAccountById(accountId, context);
-        } catch (AccountApiException e) {
-            log.warn("Ignoring payment {}: account {} does not exist", paymentId, accountId);
-            return;
-        }
+        final Account account = getAccount(accountId, context);
 
-        final Payment payment;
-        try {
-            payment = paymentApi.getPayment(paymentId, context);
-        } catch (PaymentApiException e) {
-            log.warn("Ignoring payment {}: payment does not exist", paymentId);
-            return;
-        }
+        // Recompute the account record
+        final BusinessAccountModelDao bac = businessAccountDao.createBusinessAccount(account, context);
 
-        PaymentMethod paymentMethod = null;
-        try {
-            paymentMethod = paymentApi.getPaymentMethodById(payment.getPaymentMethodId(), context);
-        } catch (PaymentApiException e) {
-            log.info("For payment {}: payment method {} does not exist", paymentId, payment.getPaymentMethodId());
-        }
+        // Recompute all invoice payments
+        final Collection<BusinessInvoicePaymentBaseModelDao> businessInvoicePayments = createBusinessInvoicePayments(account, context);
 
-        Invoice invoice = null;
-        InvoicePayment invoicePayment = null;
-        try {
-            invoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentId, context);
-            if (invoicePayment != null) {
-                invoice = invoiceApi.getInvoiceById(invoicePayment.getInvoiceId(), context);
+        // Recompute all invoice and invoice items
+        final Map<BusinessInvoiceModelDao, Collection<BusinessInvoiceItemBaseModelDao>> businessInvoices = businessInvoiceDao.createBusinessInvoicesAndInvoiceItems(account, context);
+
+        sqlDao.inTransaction(new Transaction<Void, BusinessAnalyticsSqlDao>() {
+            @Override
+            public Void inTransaction(final BusinessAnalyticsSqlDao transactional, final TransactionStatus status) throws Exception {
+                updateInTransaction(bac, businessInvoices, businessInvoicePayments, transactional, context);
+                return null;
             }
-        } catch (InvoiceApiException e) {
-            log.warn("Unable to find invoice {} for payment {}",
-                     invoicePayment != null ? invoicePayment.getInvoiceId() : "unknown", paymentId);
-        }
+        });
+    }
+
+    private void updateInTransaction(final BusinessAccountModelDao bac,
+                                     final Map<BusinessInvoiceModelDao, Collection<BusinessInvoiceItemBaseModelDao>> businessInvoices,
+                                     final Collection<BusinessInvoicePaymentBaseModelDao> businessInvoicePayments,
+                                     final BusinessAnalyticsSqlDao transactional,
+                                     final CallContext context) throws AnalyticsRefreshException {
+        rebuildInvoicePaymentsForAccountInTransaction(bac, businessInvoicePayments, transactional, context);
 
-        createPayment(account, invoice, invoicePayment, payment, paymentMethod, message, context);
+        // Update invoice balance details in BIN
+        // Note: no need to explicitly update BAC as well, since BusinessInvoiceDao will take care of it
+        businessInvoiceDao.updateInTransaction(bac, businessInvoices, transactional, context);
     }
 
-    private void createPayment(final Account account, @Nullable final Invoice invoice, @Nullable final InvoicePayment invoicePayment, final Payment payment,
-                               @Nullable final PaymentMethod paymentMethod,
-                               final String message, final InternalCallContext context) {
-        // paymentMethod may be null if the payment method has been deleted
-        final String cardCountry;
-        final String cardType;
-        final String paymentMethodString;
-        if (paymentMethod != null) {
-            final PaymentMethodPlugin pluginDetail = paymentMethod.getPluginDetail();
-            cardCountry = PaymentMethodUtils.getCardCountry(pluginDetail);
-            cardType = PaymentMethodUtils.getCardType(pluginDetail);
-            paymentMethodString = PaymentMethodUtils.getPaymentMethodType(pluginDetail);
-        } else {
-            cardCountry = null;
-            cardType = null;
-            paymentMethodString = null;
+    private void rebuildInvoicePaymentsForAccountInTransaction(final BusinessAccountModelDao bac,
+                                                               final Collection<BusinessInvoicePaymentBaseModelDao> businessInvoicePayments,
+                                                               final BusinessAnalyticsSqlDao transactional,
+                                                               final CallContext context) throws AnalyticsRefreshException {
+        for (final String tableName : BusinessInvoicePaymentBaseModelDao.ALL_INVOICE_PAYMENTS_TABLE_NAMES) {
+            transactional.deleteByAccountRecordId(tableName, bac.getAccountRecordId(), bac.getTenantRecordId(), context);
         }
 
-        // invoicePayment may be null on payment failures
-        final String invoicePaymentType;
-        final UUID linkedInvoicePaymentId;
-        final DateTime createdDate;
-        final DateTime updatedDate;
-        if (invoicePayment != null) {
-            invoicePaymentType = invoicePayment.getType().toString();
-            linkedInvoicePaymentId = invoicePayment.getLinkedInvoicePaymentId();
-            createdDate = invoicePayment.getCreatedDate();
-            updatedDate = invoicePayment.getUpdatedDate();
-        } else {
-            invoicePaymentType = null;
-            linkedInvoicePaymentId = null;
-            // TODO PIERRE
-            createdDate = clock.getUTCNow();
-            updatedDate = createdDate;
+        for (final BusinessInvoicePaymentBaseModelDao invoicePayment : businessInvoicePayments) {
+            transactional.create(invoicePayment.getTableName(), invoicePayment, context);
         }
+    }
 
-        final BusinessInvoicePaymentModelDao businessInvoicePayment = new BusinessInvoicePaymentModelDao(
-                account.getExternalKey(),
-                payment.getAmount(),
-                cardCountry,
-                cardType,
-                createdDate,
-                payment.getCurrency(),
-                payment.getEffectiveDate(),
-                payment.getInvoiceId(),
-                message,
-                payment.getId(),
-                paymentMethodString,
-                "Electronic",
-                paymentMethod == null ? null : paymentMethod.getPluginName(),
-                payment.getPaymentStatus().toString(),
-                payment.getAmount(),
-                updatedDate,
-                invoicePaymentType,
-                linkedInvoicePaymentId);
-
-        // Update the account record
-        final BusinessAccountModelDao bac = accountDao.createBusinessAccountFromAccount(account, context);
-
-        // Make sure to limit the scope of the transaction to avoid InnoDB deadlocks
-        invoicePaymentSqlDao.inTransaction(new Transaction<Void, BusinessInvoicePaymentSqlDao>() {
-            @Override
-            public Void inTransaction(final BusinessInvoicePaymentSqlDao transactional, final TransactionStatus status) throws Exception {
-                // Delete the existing payment if it exists - this is to make the call idempotent
-                transactional.deleteInvoicePayment(payment.getId().toString(), context);
-
-                // Create the bip record
-                transactional.createInvoicePayment(businessInvoicePayment, context);
-
-                if (invoice != null) {
-                    // Update bin to get the latest invoice balance
-                    final BusinessInvoiceSqlDao invoiceSqlDao = transactional.become(BusinessInvoiceSqlDao.class);
-                    invoiceDao.rebuildInvoiceInTransaction(account.getExternalKey(), invoice, invoiceSqlDao, context);
-                }
+    private Collection<BusinessInvoicePaymentBaseModelDao> createBusinessInvoicePayments(final Account account,
+                                                                                         final CallContext context) throws AnalyticsRefreshException {
+        final Collection<BusinessInvoicePaymentBaseModelDao> businessInvoicePayments = new LinkedList<BusinessInvoicePaymentBaseModelDao>();
 
-                // Update bac to get the latest account balance, total invoice balance, etc.
-                final BusinessAccountSqlDao accountSqlDao = transactional.become(BusinessAccountSqlDao.class);
-                accountDao.updateAccountInTransaction(bac, accountSqlDao, context);
+        final Collection<InvoicePayment> invoicePayments = getAccountInvoicePayments(account.getId(), context);
+        for (final InvoicePayment invoicePayment : invoicePayments) {
+            final Invoice invoice = getInvoice(invoicePayment.getInvoiceId(), context);
+            final Payment payment = getPayment(invoicePayment.getPaymentId(), context);
+            final PaymentMethod paymentMethod = getPaymentMethod(payment.getPaymentMethodId(), context);
+            final AuditLog creationAuditLog = getInvoicePaymentCreationAuditLog(invoicePayment.getId(), context);
 
-                log.info("Added payment {}", businessInvoicePayment);
-                return null;
+            final BusinessInvoicePaymentBaseModelDao businessInvoicePayment = BusinessInvoicePaymentBaseModelDao.create(account, invoice, invoicePayment, payment, paymentMethod, creationAuditLog);
+            if (businessInvoicePayment != null) {
+                businessInvoicePayments.add(businessInvoicePayment);
             }
-        });
+        }
+
+        return businessInvoicePayments;
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessOverdueStatusDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessOverdueStatusDao.java
index 16a8303..8df566f 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessOverdueStatusDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessOverdueStatusDao.java
@@ -19,83 +19,54 @@ package com.ning.billing.osgi.bundles.analytics;
 import java.util.List;
 import java.util.UUID;
 
-import javax.inject.Inject;
-
 import org.joda.time.DateTime;
+import org.osgi.service.log.LogService;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
-import org.slf4j.Logger;
-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.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
-import com.ning.billing.junction.api.Blockable;
 import com.ning.billing.junction.api.BlockingState;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessOverdueStatusSqlDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessAnalyticsSqlDao;
 import com.ning.billing.osgi.bundles.analytics.model.BusinessOverdueStatusModelDao;
-import com.ning.billing.util.callcontext.InternalCallContext;
-import com.ning.billing.util.svcapi.account.AccountInternalApi;
-import com.ning.billing.util.svcapi.entitlement.EntitlementInternalApi;
-import com.ning.billing.util.svcapi.junction.BlockingInternalApi;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillAPI;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillDataSource;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
-public class BusinessOverdueStatusDao {
-
-    private static final Logger log = LoggerFactory.getLogger(BusinessOverdueStatusDao.class);
-
-    private final BusinessOverdueStatusSqlDao overdueStatusSqlDao;
-
-    private final AccountInternalApi accountApi;
-    private final EntitlementInternalApi entitlementApi;
-    private final BlockingInternalApi blockingApi;
-
-    @Inject
-    public BusinessOverdueStatusDao(final BusinessOverdueStatusSqlDao overdueStatusSqlDao, final AccountInternalApi accountApi,
-                                    final EntitlementInternalApi entitlementApi, final BlockingInternalApi blockingApi) {
+public class BusinessOverdueStatusDao extends BusinessAnalyticsDaoBase {
 
-        this.overdueStatusSqlDao = overdueStatusSqlDao;
-        this.accountApi = accountApi;
-        this.entitlementApi = entitlementApi;
-        this.blockingApi = blockingApi;
+    public BusinessOverdueStatusDao(final OSGIKillbillLogService logService,
+                                    final OSGIKillbillAPI osgiKillbillAPI,
+                                    final OSGIKillbillDataSource osgiKillbillDataSource) {
+        super(logService, osgiKillbillAPI, osgiKillbillDataSource);
     }
 
-    public void overdueStatusChanged(final Blockable.Type objectType, final UUID objectId, final InternalCallContext context) {
-        if (Blockable.Type.SUBSCRIPTION_BUNDLE.equals(objectType)) {
+    public void update(final ObjectType objectType, final UUID objectId, final CallContext context) throws AnalyticsRefreshException {
+        if (ObjectType.BUNDLE.equals(objectType)) {
             overdueStatusChangedForBundle(objectId, context);
         } else {
-            log.info("Ignoring overdue status change for object id {} (type {})", objectId.toString(), objectType.toString());
+            logService.log(LogService.LOG_WARNING, String.format("Ignoring overdue status change for object id %s (type %s)", objectId.toString(), objectType.toString()));
         }
     }
 
-    private void overdueStatusChangedForBundle(final UUID bundleId, final InternalCallContext context) {
-        final SubscriptionBundle bundle;
-        try {
-            bundle = entitlementApi.getBundleFromId(bundleId, context);
-        } catch (EntitlementUserApiException e) {
-            log.warn("Ignoring update for bundle {}: bundle does not exist", bundleId);
-            return;
-        }
-
-        final Account account;
-        try {
-            account = accountApi.getAccountById(bundle.getAccountId(), context);
-        } catch (AccountApiException e) {
-            log.warn("Ignoring update for bundle {}: account {} does not exist", bundleId, bundle.getAccountId());
-            return;
-        }
+    private void overdueStatusChangedForBundle(final UUID bundleId, final CallContext context) throws AnalyticsRefreshException {
+        final SubscriptionBundle bundle = getSubscriptionBundle(bundleId, context);
 
+        final Account account = getAccount(bundle.getAccountId(), context);
         final String accountKey = account.getExternalKey();
         final String externalKey = bundle.getExternalKey();
 
-        overdueStatusSqlDao.inTransaction(new Transaction<Void, BusinessOverdueStatusSqlDao>() {
+        sqlDao.inTransaction(new Transaction<Void, BusinessAnalyticsSqlDao>() {
             @Override
-            public Void inTransaction(final BusinessOverdueStatusSqlDao transactional, final TransactionStatus status) throws Exception {
-                log.info("Started rebuilding overdue statuses for bundle id {}", bundleId);
-                transactional.deleteOverdueStatusesForBundle(bundleId.toString(), context);
+            public Void inTransaction(final BusinessAnalyticsSqlDao transactional, final TransactionStatus status) throws Exception {
+                // TODO pave for all bundles for that account
+                transactional.deleteByAccountRecordId(bundleId.toString(), context);
+
                 final List<BlockingState> blockingHistory = blockingApi.getBlockingHistory(bundleId, context);
                 if (blockingHistory != null && blockingHistory.size() > 0) {
                     final List<BlockingState> overdueStates = ImmutableList.<BlockingState>copyOf(blockingHistory);
@@ -105,14 +76,12 @@ public class BusinessOverdueStatusDao {
                     for (final BlockingState state : overdueStatesReversed) {
                         final BusinessOverdueStatusModelDao overdueStatus = new BusinessOverdueStatusModelDao(accountKey, bundleId, previousStartDate,
                                                                                                               externalKey, state.getTimestamp(), state.getStateName());
-                        log.info("Adding overdue state {}", overdueStatus);
-                        overdueStatusSqlDao.createOverdueStatus(overdueStatus, context);
+                        transactional.createOverdueStatus(overdueStatus, context);
 
                         previousStartDate = state.getTimestamp();
                     }
                 }
 
-                log.info("Finished rebuilding overdue statuses for bundle id {}", bundleId);
                 return null;
             }
         });
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 cdf110f..526aaa5 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
@@ -17,17 +17,14 @@
 package com.ning.billing.osgi.bundles.analytics;
 
 import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
 import java.util.UUID;
 
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
-import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.entitlement.api.SubscriptionTransitionType;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
@@ -37,59 +34,29 @@ import com.ning.billing.osgi.bundles.analytics.dao.BusinessSubscriptionTransitio
 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.InternalCallContext;
+import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.events.EffectiveSubscriptionInternalEvent;
 import com.ning.billing.util.events.SubscriptionInternalEvent;
-import com.ning.billing.util.svcapi.account.AccountInternalApi;
-import com.ning.billing.util.svcapi.entitlement.EntitlementInternalApi;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillAPI;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillDataSource;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
 
-import com.google.inject.Inject;
+public class BusinessSubscriptionTransitionDao extends BusinessAnalyticsDaoBase {
 
-public class BusinessSubscriptionTransitionDao {
-
-    private static final Logger log = LoggerFactory.getLogger(BusinessSubscriptionTransitionDao.class);
-
-    private final BusinessSubscriptionTransitionSqlDao sqlDao;
-    private final EntitlementInternalApi entitlementApi;
-    private final AccountInternalApi accountApi;
-    private final CatalogService catalogService;
-
-    @Inject
-    public BusinessSubscriptionTransitionDao(final BusinessSubscriptionTransitionSqlDao sqlDao,
-                                             final CatalogService catalogService,
-                                             final EntitlementInternalApi entitlementApi,
-                                             final AccountInternalApi accountApi) {
-        this.sqlDao = sqlDao;
-        this.catalogService = catalogService;
-        this.entitlementApi = entitlementApi;
-        this.accountApi = accountApi;
+    public BusinessSubscriptionTransitionDao(final OSGIKillbillLogService logService, final OSGIKillbillAPI osgiKillbillAPI,
+                                             final OSGIKillbillDataSource osgiKillbillDataSource) {
+        super(logService, osgiKillbillAPI, osgiKillbillDataSource);
     }
 
-    public void rebuildTransitionsForBundle(final UUID bundleId, final InternalCallContext context) {
-        final SubscriptionBundle bundle;
-        try {
-            bundle = entitlementApi.getBundleFromId(bundleId, context);
-        } catch (EntitlementUserApiException e) {
-            log.warn("Ignoring update for bundle {}: bundle does not exist", bundleId);
-            return;
-        }
-
-        final Account account;
-        try {
-            account = accountApi.getAccountById(bundle.getAccountId(), context);
-        } catch (AccountApiException e) {
-            log.warn("Ignoring update for bundle {}: account {} does not exist", bundleId, bundle.getAccountId());
-            return;
-        }
-
-        final List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(bundleId, context);
-
+    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();
 
         sqlDao.inTransaction(new Transaction<Void, BusinessSubscriptionTransitionSqlDao>() {
             @Override
             public Void inTransaction(final BusinessSubscriptionTransitionSqlDao transactional, final TransactionStatus status) throws Exception {
-                log.info("Started rebuilding transitions for bundle id {}", bundleId);
                 transactional.deleteTransitionsForBundle(bundleId.toString(), context);
 
                 final ArrayList<BusinessSubscriptionTransitionModelDao> transitions = new ArrayList<BusinessSubscriptionTransitionModelDao>();
@@ -118,7 +85,6 @@ public class BusinessSubscriptionTransitionDao {
 
                         transactional.createTransition(transition, context);
                         transitions.add(transition);
-                        log.info("Adding transition {}", transition);
 
                         // We need to manually add the system cancel event
                         if (SubscriptionTransitionType.CANCEL.equals(event.getTransitionType())) {
@@ -138,12 +104,10 @@ public class BusinessSubscriptionTransitionDao {
                             );
                             transactional.createTransition(systemCancelTransition, context);
                             transitions.add(systemCancelTransition);
-                            log.info("Adding transition {}", systemCancelTransition);
                         }
                     }
                 }
 
-                log.info("Finished rebuilding transitions for bundle id {}", bundleId);
                 return null;
             }
         });
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/DefaultAnalyticsDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/DefaultAnalyticsDao.java
index b710f85..2ecbc5f 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/DefaultAnalyticsDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/DefaultAnalyticsDao.java
@@ -18,10 +18,7 @@ package com.ning.billing.osgi.bundles.analytics.dao;
 
 import java.util.List;
 
-import javax.inject.Inject;
-
 import com.ning.billing.analytics.api.TimeSeriesData;
-import com.ning.billing.osgi.bundles.analytics.api.DefaultTimeSeriesData;
 import com.ning.billing.osgi.bundles.analytics.model.BusinessAccountModelDao;
 import com.ning.billing.osgi.bundles.analytics.model.BusinessAccountTagModelDao;
 import com.ning.billing.osgi.bundles.analytics.model.BusinessInvoiceItemModelDao;
@@ -33,78 +30,53 @@ import com.ning.billing.util.callcontext.InternalTenantContext;
 
 public class DefaultAnalyticsDao implements AnalyticsDao {
 
-    private final BusinessAccountSqlDao accountSqlDao;
-    private final BusinessSubscriptionTransitionSqlDao subscriptionTransitionSqlDao;
-    private final BusinessInvoiceSqlDao invoiceSqlDao;
-    private final BusinessInvoiceItemSqlDao invoiceItemSqlDao;
-    private final BusinessAccountTagSqlDao accountTagSqlDao;
-    private final BusinessOverdueStatusSqlDao overdueStatusSqlDao;
-    private final BusinessInvoicePaymentSqlDao invoicePaymentSqlDao;
-
-    @Inject
-    public DefaultAnalyticsDao(final BusinessAccountSqlDao accountSqlDao,
-                               final BusinessSubscriptionTransitionSqlDao subscriptionTransitionSqlDao,
-                               final BusinessInvoiceSqlDao invoiceSqlDao,
-                               final BusinessInvoiceItemSqlDao invoiceItemSqlDao,
-                               final BusinessAccountTagSqlDao accountTagSqlDao,
-                               final BusinessOverdueStatusSqlDao overdueStatusSqlDao,
-                               final BusinessInvoicePaymentSqlDao invoicePaymentSqlDao) {
-        this.accountSqlDao = accountSqlDao;
-        this.subscriptionTransitionSqlDao = subscriptionTransitionSqlDao;
-        this.invoiceSqlDao = invoiceSqlDao;
-        this.invoiceItemSqlDao = invoiceItemSqlDao;
-        this.accountTagSqlDao = accountTagSqlDao;
-        this.overdueStatusSqlDao = overdueStatusSqlDao;
-        this.invoicePaymentSqlDao = invoicePaymentSqlDao;
-    }
-
     @Override
     public TimeSeriesData getAccountsCreatedOverTime(final InternalTenantContext context) {
-        return new DefaultTimeSeriesData(accountSqlDao.getAccountsCreatedOverTime(context));
+        return null;
     }
 
     @Override
     public TimeSeriesData getSubscriptionsCreatedOverTime(final String productType, final String slug, final InternalTenantContext context) {
-        return new DefaultTimeSeriesData(subscriptionTransitionSqlDao.getSubscriptionsCreatedOverTime(productType, slug, context));
+        return null;
     }
 
     @Override
     public BusinessAccountModelDao getAccountByKey(final String accountKey, final InternalTenantContext context) {
-        return accountSqlDao.getAccountByKey(accountKey, context);
+        return null;
     }
 
     @Override
     public List<BusinessSubscriptionTransitionModelDao> getTransitionsByKey(final String externalKey, final InternalTenantContext context) {
-        return subscriptionTransitionSqlDao.getTransitionsByKey(externalKey, context);
+        return null;
     }
 
     @Override
     public List<BusinessSubscriptionTransitionModelDao> getTransitionsForAccount(final String accountKey, final InternalTenantContext context) {
-        return subscriptionTransitionSqlDao.getTransitionsForAccount(accountKey, context);
+        return null;
     }
 
     @Override
     public List<BusinessInvoiceModelDao> getInvoicesByKey(final String accountKey, final InternalTenantContext context) {
-        return invoiceSqlDao.getInvoicesForAccountByKey(accountKey, context);
+        return null;
     }
 
     @Override
-    public List<BusinessAccountTagModelDao> getTagsForAccount(final String accountKey, final InternalTenantContext context) {
-        return accountTagSqlDao.getTagsForAccountByKey(accountKey, context);
+    public List<BusinessInvoiceItemModelDao> getInvoiceItemsForInvoice(final String invoiceId, final InternalTenantContext context) {
+        return null;
     }
 
     @Override
-    public List<BusinessInvoiceItemModelDao> getInvoiceItemsForInvoice(final String invoiceId, final InternalTenantContext context) {
-        return invoiceItemSqlDao.getInvoiceItemsForInvoice(invoiceId, context);
+    public List<BusinessInvoicePaymentModelDao> getInvoicePaymentsForAccountByKey(final String accountKey, final InternalTenantContext context) {
+        return null;
     }
 
     @Override
     public List<BusinessOverdueStatusModelDao> getOverdueStatusesForBundleByKey(final String externalKey, final InternalTenantContext context) {
-        return overdueStatusSqlDao.getOverdueStatusesForBundleByKey(externalKey, context);
+        return null;
     }
 
     @Override
-    public List<BusinessInvoicePaymentModelDao> getInvoicePaymentsForAccountByKey(final String accountKey, final InternalTenantContext context) {
-        return invoicePaymentSqlDao.getInvoicePaymentsForAccountByKey(accountKey, context);
+    public List<BusinessAccountTagModelDao> getTagsForAccount(final String accountKey, final InternalTenantContext context) {
+        return null;
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessAccountFieldModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessAccountFieldModelDao.java
index 5384aff..c6ae5db 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessAccountFieldModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessAccountFieldModelDao.java
@@ -18,70 +18,40 @@ package com.ning.billing.osgi.bundles.analytics.model;
 
 import java.util.UUID;
 
-public class BusinessAccountFieldModelDao extends BusinessFieldModelDao {
-
-    private final UUID accountId;
-    private final String accountKey;
-
-    public BusinessAccountFieldModelDao(final UUID accountId, final String accountKey, final String name, final String value) {
-        super(accountId, name, value);
-        this.accountId = accountId;
-        this.accountKey = accountKey;
-    }
-
-    public UUID getAccountId() {
-        return accountId;
-    }
-
-    public String getAccountKey() {
-        return accountKey;
-    }
+import org.joda.time.DateTime;
 
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("BusinessAccountFieldModelDao");
-        sb.append("{accountId='").append(accountId).append('\'');
-        sb.append(", accountKey='").append(accountKey).append('\'');
-        sb.append(", name='").append(getName()).append('\'');
-        sb.append(", value='").append(getValue()).append('\'');
-        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 BusinessAccountFieldModelDao that = (BusinessAccountFieldModelDao) o;
-
-        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 (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
-            return false;
-        }
-        if (getValue() != null ? !getValue().equals(that.getValue()) : that.getValue() != null) {
-            return false;
-        }
+public class BusinessAccountFieldModelDao extends BusinessFieldModelDao {
 
-        return true;
+    private static final String ACCOUNT_FIELDS_TABLE_NAME = "bac_fields";
+
+    public BusinessAccountFieldModelDao(final Long customFieldRecordId,
+                                        final String name,
+                                        final String value,
+                                        final DateTime createdDate,
+                                        final String createdBy,
+                                        final String createdReasonCode,
+                                        final String createdComments,
+                                        final UUID accountId,
+                                        final String accountName,
+                                        final String accountExternalKey,
+                                        final Long accountRecordId,
+                                        final Long tenantRecordId) {
+        super(customFieldRecordId,
+              name,
+              value,
+              createdDate,
+              createdBy,
+              createdReasonCode,
+              createdComments,
+              accountId,
+              accountName,
+              accountExternalKey,
+              accountRecordId,
+              tenantRecordId);
     }
 
     @Override
-    public int hashCode() {
-        int result = accountId != null ? accountId.hashCode() : 0;
-        result = 31 * result + (accountKey != null ? accountKey.hashCode() : 0);
-        result = 31 * result + (getName() != null ? getName().hashCode() : 0);
-        result = 31 * result + (getValue() != null ? getValue().hashCode() : 0);
-        return result;
+    public String getTableName() {
+        return ACCOUNT_FIELDS_TABLE_NAME;
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessAccountModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessAccountModelDao.java
index 3536f29..68e79dd 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessAccountModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessAccountModelDao.java
@@ -19,166 +19,229 @@ package com.ning.billing.osgi.bundles.analytics.model;
 import java.math.BigDecimal;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 
 import com.ning.billing.account.api.Account;
-import com.ning.billing.osgi.bundles.analytics.utils.Rounder;
-import com.ning.billing.util.entity.EntityBase;
-
-import com.google.common.base.Objects;
-
-public class BusinessAccountModelDao extends EntityBase {
-
-    private final UUID accountId;
-    private String key;
-    private String name;
-    private BigDecimal balance;
-    private LocalDate lastInvoiceDate;
-    private BigDecimal totalInvoiceBalance;
-    private String lastPaymentStatus;
-    private String paymentMethod;
-    private String creditCardType;
-    private String billingAddressCountry;
-    private String currency;
-
-    public BusinessAccountModelDao(final UUID accountId, final String key, final String name, final BigDecimal balance,
-                                   final LocalDate lastInvoiceDate, final BigDecimal totalInvoiceBalance, final String lastPaymentStatus,
-                                   final String paymentMethod, final String creditCardType, final String billingAddressCountry,
-                                   final String currency, final DateTime createdDt, final DateTime updatedDt) {
-        super(accountId, createdDt, updatedDt);
-        this.accountId = accountId;
-        this.key = key;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.payment.api.Payment;
+import com.ning.billing.util.audit.AuditLog;
+
+public class BusinessAccountModelDao extends BusinessModelDaoBase {
+
+    private static final String ACCOUNTS_TABLE_NAME = "bac";
+
+    private final String email;
+    private final Integer firstNameLength;
+    private final String currency;
+    private final Integer billingCycleDayLocal;
+    private final String address1;
+    private final String address2;
+    private final String companyName;
+    private final String city;
+    private final String stateOrProvince;
+    private final String country;
+    private final String postalCode;
+    private final String phone;
+    private final Boolean isMigrated;
+    private final Boolean isNotifiedForInvoices;
+    private final BigDecimal balance;
+    private final LocalDate lastInvoiceDate;
+    private final DateTime lastPaymentDate;
+    private final String lastPaymentStatus;
+
+    private BusinessAccountModelDao(final String email,
+                                    final Integer firstNameLength,
+                                    final String currency,
+                                    final Integer billingCycleDayLocal,
+                                    final String address1,
+                                    final String address2,
+                                    final String companyName,
+                                    final String city,
+                                    final String stateOrProvince,
+                                    final String country,
+                                    final String postalCode,
+                                    final String phone,
+                                    final Boolean isMigrated,
+                                    final Boolean notifiedForInvoices,
+                                    final BigDecimal balance,
+                                    final LocalDate lastInvoiceDate,
+                                    final DateTime lastPaymentDate,
+                                    final String lastPaymentStatus,
+                                    final DateTime createdDate,
+                                    final String createdBy,
+                                    final String createdReasonCode,
+                                    final String createdComments,
+                                    final UUID accountId,
+                                    final String accountName,
+                                    final String accountExternalKey,
+                                    final Long accountRecordId,
+                                    final Long tenantRecordId) {
+        super(createdDate,
+              createdBy,
+              createdReasonCode,
+              createdComments,
+              accountId,
+              accountName,
+              accountExternalKey,
+              accountRecordId,
+              tenantRecordId);
+        this.email = email;
+        this.firstNameLength = firstNameLength;
+        this.currency = currency;
+        this.billingCycleDayLocal = billingCycleDayLocal;
+        this.address1 = address1;
+        this.address2 = address2;
+        this.companyName = companyName;
+        this.city = city;
+        this.stateOrProvince = stateOrProvince;
+        this.country = country;
+        this.postalCode = postalCode;
+        this.phone = phone;
+        this.isMigrated = isMigrated;
+        this.isNotifiedForInvoices = notifiedForInvoices;
         this.balance = balance;
-        this.billingAddressCountry = billingAddressCountry;
-        this.creditCardType = creditCardType;
         this.lastInvoiceDate = lastInvoiceDate;
+        this.lastPaymentDate = lastPaymentDate;
         this.lastPaymentStatus = lastPaymentStatus;
-        this.name = name;
-        this.paymentMethod = paymentMethod;
-        this.totalInvoiceBalance = totalInvoiceBalance;
-        this.currency = currency;
     }
 
-    public BusinessAccountModelDao(final Account account) {
-        super(account.getId(), account.getCreatedDate(), account.getUpdatedDate());
-        this.accountId = account.getId();
-        this.name = account.getName();
-        this.key = account.getExternalKey();
-        if (account.getCurrency() != null) {
-            this.currency = account.getCurrency().toString();
-        }
+    public BusinessAccountModelDao(final Account account,
+                                   final BigDecimal balance,
+                                   @Nullable final Invoice lastInvoice,
+                                   @Nullable final Payment lastPayment,
+                                   final AuditLog creationAuditLog) {
+        this(account.getEmail(),
+             account.getFirstNameLength(),
+             account.getCurrency() == null ? null : account.getCurrency().toString(),
+             account.getBillCycleDayLocal(),
+             account.getAddress1(),
+             account.getAddress2(),
+             account.getCompanyName(),
+             account.getCity(),
+             account.getStateOrProvince(),
+             account.getCountry(),
+             account.getPostalCode(),
+             account.getPhone(),
+             account.isMigrated(),
+             account.isNotifiedForInvoices(),
+             balance,
+             lastInvoice == null ? null : lastInvoice.getInvoiceDate(),
+             lastPayment == null ? null : lastPayment.getEffectiveDate(),
+             lastPayment == null ? null : lastPayment.getPaymentStatus().toString(),
+             account.getCreatedDate(),
+             creationAuditLog.getUserName(),
+             creationAuditLog.getReasonCode(),
+             creationAuditLog.getComment(),
+             account.getId(),
+             account.getName(),
+             account.getExternalKey(),
+             // TODO
+             null,
+             null);
     }
 
-    public UUID getAccountId() {
-        return accountId;
-    }
-
-    public String getKey() {
-        return key;
-    }
-
-    public void setKey(final String key) {
-        this.key = key;
-    }
-
-    public BigDecimal getBalance() {
-        return Objects.firstNonNull(balance, BigDecimal.ZERO);
+    @Override
+    public String getTableName() {
+        return ACCOUNTS_TABLE_NAME;
     }
 
-    public Double getRoundedBalance() {
-        return Rounder.round(balance);
+    public String getEmail() {
+        return email;
     }
 
-    public void setBalance(final BigDecimal balance) {
-        this.balance = balance;
+    public Integer getFirstNameLength() {
+        return firstNameLength;
     }
 
-    public String getBillingAddressCountry() {
-        return billingAddressCountry;
+    public String getCurrency() {
+        return currency;
     }
 
-    public void setBillingAddressCountry(final String billingAddressCountry) {
-        this.billingAddressCountry = billingAddressCountry;
+    public Integer getBillingCycleDayLocal() {
+        return billingCycleDayLocal;
     }
 
-    public String getCreditCardType() {
-        return creditCardType;
+    public String getAddress1() {
+        return address1;
     }
 
-    public void setCreditCardType(final String creditCardType) {
-        this.creditCardType = creditCardType;
+    public String getAddress2() {
+        return address2;
     }
 
-    public String getCurrency() {
-        return currency;
+    public String getCompanyName() {
+        return companyName;
     }
 
-    public void setCurrency(final String currency) {
-        this.currency = currency;
+    public String getCity() {
+        return city;
     }
 
-    public LocalDate getLastInvoiceDate() {
-        return lastInvoiceDate;
+    public String getStateOrProvince() {
+        return stateOrProvince;
     }
 
-    public void setLastInvoiceDate(final LocalDate lastInvoiceDate) {
-        this.lastInvoiceDate = lastInvoiceDate;
+    public String getCountry() {
+        return country;
     }
 
-    public String getLastPaymentStatus() {
-        return lastPaymentStatus;
+    public String getPostalCode() {
+        return postalCode;
     }
 
-    public void setLastPaymentStatus(final String lastPaymentStatus) {
-        this.lastPaymentStatus = lastPaymentStatus;
+    public String getPhone() {
+        return phone;
     }
 
-    public String getName() {
-        return name;
+    public Boolean getMigrated() {
+        return isMigrated;
     }
 
-    public void setName(final String name) {
-        this.name = name;
+    public Boolean getNotifiedForInvoices() {
+        return isNotifiedForInvoices;
     }
 
-    public String getPaymentMethod() {
-        return paymentMethod;
-    }
-
-    public void setPaymentMethod(final String paymentMethod) {
-        this.paymentMethod = paymentMethod;
+    public BigDecimal getBalance() {
+        return balance;
     }
 
-    public BigDecimal getTotalInvoiceBalance() {
-        return Objects.firstNonNull(totalInvoiceBalance, BigDecimal.ZERO);
+    public LocalDate getLastInvoiceDate() {
+        return lastInvoiceDate;
     }
 
-    public Double getRoundedTotalInvoiceBalance() {
-        return Rounder.round(totalInvoiceBalance);
+    public DateTime getLastPaymentDate() {
+        return lastPaymentDate;
     }
 
-    public void setTotalInvoiceBalance(final BigDecimal totalInvoiceBalance) {
-        this.totalInvoiceBalance = totalInvoiceBalance;
+    public String getLastPaymentStatus() {
+        return lastPaymentStatus;
     }
 
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         sb.append("BusinessAccountModelDao");
-        sb.append("{balance=").append(balance);
-        sb.append(", createdDate=").append(createdDate);
-        sb.append(", updatedDate=").append(updatedDate);
-        sb.append(", accountId='").append(accountId).append('\'');
-        sb.append(", key='").append(key).append('\'');
-        sb.append(", name='").append(name).append('\'');
+        sb.append("{email='").append(email).append('\'');
+        sb.append(", firstNameLength=").append(firstNameLength);
+        sb.append(", currency='").append(currency).append('\'');
+        sb.append(", billingCycleDayLocal=").append(billingCycleDayLocal);
+        sb.append(", address1='").append(address1).append('\'');
+        sb.append(", address2='").append(address2).append('\'');
+        sb.append(", companyName='").append(companyName).append('\'');
+        sb.append(", city='").append(city).append('\'');
+        sb.append(", stateOrProvince='").append(stateOrProvince).append('\'');
+        sb.append(", country='").append(country).append('\'');
+        sb.append(", postalCode='").append(postalCode).append('\'');
+        sb.append(", phone='").append(phone).append('\'');
+        sb.append(", isMigrated=").append(isMigrated);
+        sb.append(", isNotifiedForInvoices=").append(isNotifiedForInvoices);
+        sb.append(", balance=").append(balance);
         sb.append(", lastInvoiceDate=").append(lastInvoiceDate);
-        sb.append(", totalInvoiceBalance=").append(totalInvoiceBalance);
+        sb.append(", lastPaymentDate=").append(lastPaymentDate);
         sb.append(", lastPaymentStatus='").append(lastPaymentStatus).append('\'');
-        sb.append(", paymentMethod='").append(paymentMethod).append('\'');
-        sb.append(", creditCardType='").append(creditCardType).append('\'');
-        sb.append(", billingAddressCountry='").append(billingAddressCountry).append('\'');
-        sb.append(", currency='").append(currency).append('\'');
         sb.append('}');
         return sb.toString();
     }
@@ -191,46 +254,64 @@ public class BusinessAccountModelDao extends EntityBase {
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
+        if (!super.equals(o)) {
+            return false;
+        }
 
         final BusinessAccountModelDao that = (BusinessAccountModelDao) o;
 
-        if (balance == null ? that.balance != null : balance.compareTo(that.balance) != 0) {
+        if (address1 != null ? !address1.equals(that.address1) : that.address1 != null) {
+            return false;
+        }
+        if (address2 != null ? !address2.equals(that.address2) : that.address2 != null) {
             return false;
         }
-        if (billingAddressCountry != null ? !billingAddressCountry.equals(that.billingAddressCountry) : that.billingAddressCountry != null) {
+        if (balance != null ? !balance.equals(that.balance) : that.balance != null) {
             return false;
         }
-        if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
+        if (billingCycleDayLocal != null ? !billingCycleDayLocal.equals(that.billingCycleDayLocal) : that.billingCycleDayLocal != null) {
             return false;
         }
-        if (creditCardType != null ? !creditCardType.equals(that.creditCardType) : that.creditCardType != null) {
+        if (city != null ? !city.equals(that.city) : that.city != null) {
+            return false;
+        }
+        if (companyName != null ? !companyName.equals(that.companyName) : that.companyName != null) {
+            return false;
+        }
+        if (country != null ? !country.equals(that.country) : that.country != null) {
             return false;
         }
         if (currency != null ? !currency.equals(that.currency) : that.currency != null) {
             return false;
         }
-        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
+        if (email != null ? !email.equals(that.email) : that.email != null) {
             return false;
         }
-        if (key != null ? !key.equals(that.key) : that.key != null) {
+        if (firstNameLength != null ? !firstNameLength.equals(that.firstNameLength) : that.firstNameLength != null) {
             return false;
         }
-        if (lastInvoiceDate != null ? lastInvoiceDate.compareTo(that.lastInvoiceDate) != 0 : that.lastInvoiceDate != null) {
+        if (isMigrated != null ? !isMigrated.equals(that.isMigrated) : that.isMigrated != null) {
             return false;
         }
-        if (lastPaymentStatus != null ? !lastPaymentStatus.equals(that.lastPaymentStatus) : that.lastPaymentStatus != null) {
+        if (isNotifiedForInvoices != null ? !isNotifiedForInvoices.equals(that.isNotifiedForInvoices) : that.isNotifiedForInvoices != null) {
+            return false;
+        }
+        if (lastInvoiceDate != null ? !lastInvoiceDate.equals(that.lastInvoiceDate) : that.lastInvoiceDate != null) {
+            return false;
+        }
+        if (lastPaymentDate != null ? !lastPaymentDate.equals(that.lastPaymentDate) : that.lastPaymentDate != null) {
             return false;
         }
-        if (name != null ? !name.equals(that.name) : that.name != null) {
+        if (lastPaymentStatus != null ? !lastPaymentStatus.equals(that.lastPaymentStatus) : that.lastPaymentStatus != null) {
             return false;
         }
-        if (paymentMethod != null ? !paymentMethod.equals(that.paymentMethod) : that.paymentMethod != null) {
+        if (phone != null ? !phone.equals(that.phone) : that.phone != null) {
             return false;
         }
-        if (totalInvoiceBalance == null ? that.totalInvoiceBalance != null : totalInvoiceBalance.compareTo(that.totalInvoiceBalance) != 0) {
+        if (postalCode != null ? !postalCode.equals(that.postalCode) : that.postalCode != null) {
             return false;
         }
-        if (updatedDate != null ? updatedDate.compareTo(that.updatedDate) != 0 : that.updatedDate != null) {
+        if (stateOrProvince != null ? !stateOrProvince.equals(that.stateOrProvince) : that.stateOrProvince != null) {
             return false;
         }
 
@@ -239,19 +320,25 @@ public class BusinessAccountModelDao extends EntityBase {
 
     @Override
     public int hashCode() {
-        int result = createdDate != null ? createdDate.hashCode() : 0;
-        result = 31 * result + (updatedDate != null ? updatedDate.hashCode() : 0);
-        result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
-        result = 31 * result + (key != null ? key.hashCode() : 0);
-        result = 31 * result + (name != null ? name.hashCode() : 0);
+        int result = super.hashCode();
+        result = 31 * result + (email != null ? email.hashCode() : 0);
+        result = 31 * result + (firstNameLength != null ? firstNameLength.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (billingCycleDayLocal != null ? billingCycleDayLocal.hashCode() : 0);
+        result = 31 * result + (address1 != null ? address1.hashCode() : 0);
+        result = 31 * result + (address2 != null ? address2.hashCode() : 0);
+        result = 31 * result + (companyName != null ? companyName.hashCode() : 0);
+        result = 31 * result + (city != null ? city.hashCode() : 0);
+        result = 31 * result + (stateOrProvince != null ? stateOrProvince.hashCode() : 0);
+        result = 31 * result + (country != null ? country.hashCode() : 0);
+        result = 31 * result + (postalCode != null ? postalCode.hashCode() : 0);
+        result = 31 * result + (phone != null ? phone.hashCode() : 0);
+        result = 31 * result + (isMigrated != null ? isMigrated.hashCode() : 0);
+        result = 31 * result + (isNotifiedForInvoices != null ? isNotifiedForInvoices.hashCode() : 0);
         result = 31 * result + (balance != null ? balance.hashCode() : 0);
         result = 31 * result + (lastInvoiceDate != null ? lastInvoiceDate.hashCode() : 0);
-        result = 31 * result + (totalInvoiceBalance != null ? totalInvoiceBalance.hashCode() : 0);
+        result = 31 * result + (lastPaymentDate != null ? lastPaymentDate.hashCode() : 0);
         result = 31 * result + (lastPaymentStatus != null ? lastPaymentStatus.hashCode() : 0);
-        result = 31 * result + (paymentMethod != null ? paymentMethod.hashCode() : 0);
-        result = 31 * result + (creditCardType != null ? creditCardType.hashCode() : 0);
-        result = 31 * result + (billingAddressCountry != null ? billingAddressCountry.hashCode() : 0);
-        result = 31 * result + (currency != null ? currency.hashCode() : 0);
         return result;
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessAccountTagModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessAccountTagModelDao.java
index 8b419d7..4a973d5 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessAccountTagModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessAccountTagModelDao.java
@@ -18,65 +18,36 @@ package com.ning.billing.osgi.bundles.analytics.model;
 
 import java.util.UUID;
 
-public class BusinessAccountTagModelDao extends BusinessTagModelDao {
-
-    private final UUID accountId;
-    private final String accountKey;
-
-    public BusinessAccountTagModelDao(final UUID accountId, final String accountKey, final String name) {
-        super(name);
-        this.accountId = accountId;
-        this.accountKey = accountKey;
-    }
-
-    public UUID getAccountId() {
-        return accountId;
-    }
-
-    public String getAccountKey() {
-        return accountKey;
-    }
+import org.joda.time.DateTime;
 
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("BusinessAccountTagModelDao");
-        sb.append("{accountId='").append(accountId).append('\'');
-        sb.append(", accountKey='").append(accountKey).append('\'');
-        sb.append(", name='").append(getName()).append('\'');
-        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 BusinessAccountTagModelDao that = (BusinessAccountTagModelDao) o;
-
-        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 (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
-            return false;
-        }
+public class BusinessAccountTagModelDao extends BusinessTagModelDao {
 
-        return true;
+    private static final String ACCOUNT_TAGS_TABLE_NAME = "bac_tags";
+
+    public BusinessAccountTagModelDao(final String name,
+                                      final DateTime createdDate,
+                                      final String createdBy,
+                                      final String createdReasonCode,
+                                      final String createdComments,
+                                      final UUID accountId,
+                                      final String accountName,
+                                      final String accountExternalKey,
+                                      final Long accountRecordId,
+                                      final Long tenantRecordId) {
+        super(name,
+              createdDate,
+              createdBy,
+              createdReasonCode,
+              createdComments,
+              accountId,
+              accountName,
+              accountExternalKey,
+              accountRecordId,
+              tenantRecordId);
     }
 
     @Override
-    public int hashCode() {
-        int result = accountId != null ? accountId.hashCode() : 0;
-        result = 31 * result + (accountKey != null ? accountKey.hashCode() : 0);
-        result = 31 * result + (getName() != null ? getName().hashCode() : 0);
-        return result;
+    public String getTableName() {
+        return ACCOUNT_TAGS_TABLE_NAME;
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessFieldModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessFieldModelDao.java
index ed0293a..dba17ad 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessFieldModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessFieldModelDao.java
@@ -18,19 +18,44 @@ package com.ning.billing.osgi.bundles.analytics.model;
 
 import java.util.UUID;
 
-import com.ning.billing.util.entity.EntityBase;
+import org.joda.time.DateTime;
 
-public abstract class BusinessFieldModelDao extends EntityBase {
+public abstract class BusinessFieldModelDao extends BusinessModelDaoBase {
 
+    private final Long customFieldRecordId;
     private final String name;
     private final String value;
 
-    public BusinessFieldModelDao(final UUID id, final String name, final String value) {
-        super(id);
+    protected BusinessFieldModelDao(final Long customFieldRecordId,
+                                    final String name,
+                                    final String value,
+                                    final DateTime createdDate,
+                                    final String createdBy,
+                                    final String createdReasonCode,
+                                    final String createdComments,
+                                    final UUID accountId,
+                                    final String accountName,
+                                    final String accountExternalKey,
+                                    final Long accountRecordId,
+                                    final Long tenantRecordId) {
+        super(createdDate,
+              createdBy,
+              createdReasonCode,
+              createdComments,
+              accountId,
+              accountName,
+              accountExternalKey,
+              accountRecordId,
+              tenantRecordId);
+        this.customFieldRecordId = customFieldRecordId;
         this.name = name;
         this.value = value;
     }
 
+    public Long getCustomFieldRecordId() {
+        return customFieldRecordId;
+    }
+
     public String getName() {
         return name;
     }
@@ -38,4 +63,51 @@ public abstract class BusinessFieldModelDao extends EntityBase {
     public String getValue() {
         return value;
     }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessFieldModelDao");
+        sb.append("{customFieldRecordId=").append(customFieldRecordId);
+        sb.append(", name='").append(name).append('\'');
+        sb.append(", value='").append(value).append('\'');
+        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 BusinessFieldModelDao that = (BusinessFieldModelDao) o;
+
+        if (customFieldRecordId != null ? !customFieldRecordId.equals(that.customFieldRecordId) : that.customFieldRecordId != null) {
+            return false;
+        }
+        if (name != null ? !name.equals(that.name) : that.name != null) {
+            return false;
+        }
+        if (value != null ? !value.equals(that.value) : that.value != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (customFieldRecordId != null ? customFieldRecordId.hashCode() : 0);
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        result = 31 * result + (value != null ? value.hashCode() : 0);
+        return result;
+    }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceAdjustmentModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceAdjustmentModelDao.java
new file mode 100644
index 0000000..4605b08
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceAdjustmentModelDao.java
@@ -0,0 +1,51 @@
+/*
+ * 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.osgi.bundles.analytics.model;
+
+import javax.annotation.Nullable;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.util.audit.AuditLog;
+
+public class BusinessInvoiceAdjustmentModelDao extends BusinessInvoiceItemBaseModelDao {
+
+    public BusinessInvoiceAdjustmentModelDao(final Account account,
+                                             final Invoice invoice,
+                                             final InvoiceItem invoiceItem,
+                                             @Nullable final SubscriptionBundle bundle,
+                                             @Nullable final Plan plan,
+                                             @Nullable final PlanPhase planPhase,
+                                             final AuditLog creationAuditLog) {
+        super(account,
+              invoice,
+              invoiceItem,
+              bundle,
+              plan,
+              planPhase,
+              creationAuditLog);
+    }
+
+    @Override
+    public String getTableName() {
+        return INVOICE_ADJUSTMENTS_TABLE_NAME;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceFieldModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceFieldModelDao.java
index 5bdaeab..1d6bafe 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceFieldModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceFieldModelDao.java
@@ -18,15 +18,47 @@ package com.ning.billing.osgi.bundles.analytics.model;
 
 import java.util.UUID;
 
+import org.joda.time.DateTime;
+
 public class BusinessInvoiceFieldModelDao extends BusinessFieldModelDao {
 
+    private static final String INVOICE_FIELDS_TABLE_NAME = "bin_fields";
+
     private final UUID invoiceId;
 
-    public BusinessInvoiceFieldModelDao(final UUID invoiceId, final String name, final String value) {
-        super(invoiceId, name, value);
+    public BusinessInvoiceFieldModelDao(final Long customFieldRecordId,
+                                        final UUID invoiceId,
+                                        final String name,
+                                        final String value,
+                                        final DateTime createdDate,
+                                        final String createdBy,
+                                        final String createdReasonCode,
+                                        final String createdComments,
+                                        final UUID accountId,
+                                        final String accountName,
+                                        final String accountExternalKey,
+                                        final Long accountRecordId,
+                                        final Long tenantRecordId) {
+        super(customFieldRecordId,
+              name,
+              value,
+              createdDate,
+              createdBy,
+              createdReasonCode,
+              createdComments,
+              accountId,
+              accountName,
+              accountExternalKey,
+              accountRecordId,
+              tenantRecordId);
         this.invoiceId = invoiceId;
     }
 
+    @Override
+    public String getTableName() {
+        return INVOICE_FIELDS_TABLE_NAME;
+    }
+
     public UUID getInvoiceId() {
         return invoiceId;
     }
@@ -35,7 +67,8 @@ public class BusinessInvoiceFieldModelDao extends BusinessFieldModelDao {
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         sb.append("BusinessInvoiceFieldModelDao");
-        sb.append("{invoiceId='").append(invoiceId).append('\'');
+        sb.append("{invoiceId=").append(invoiceId);
+        sb.append(", customFieldRecordId=").append(getCustomFieldRecordId());
         sb.append(", name='").append(getName()).append('\'');
         sb.append(", value='").append(getValue()).append('\'');
         sb.append('}');
@@ -50,27 +83,23 @@ public class BusinessInvoiceFieldModelDao extends BusinessFieldModelDao {
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
+        if (!super.equals(o)) {
+            return false;
+        }
 
         final BusinessInvoiceFieldModelDao that = (BusinessInvoiceFieldModelDao) o;
 
         if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) {
             return false;
         }
-        if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
-            return false;
-        }
-        if (getValue() != null ? !getValue().equals(that.getValue()) : that.getValue() != null) {
-            return false;
-        }
 
         return true;
     }
 
     @Override
     public int hashCode() {
-        int result = invoiceId != null ? invoiceId.hashCode() : 0;
-        result = 31 * result + (getName() != null ? getName().hashCode() : 0);
-        result = 31 * result + (getValue() != null ? getValue().hashCode() : 0);
+        int result = super.hashCode();
+        result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
         return result;
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceItemAdjustmentModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceItemAdjustmentModelDao.java
new file mode 100644
index 0000000..cf1c517
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceItemAdjustmentModelDao.java
@@ -0,0 +1,51 @@
+/*
+ * 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.osgi.bundles.analytics.model;
+
+import javax.annotation.Nullable;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.util.audit.AuditLog;
+
+public class BusinessInvoiceItemAdjustmentModelDao extends BusinessInvoiceItemBaseModelDao {
+
+    public BusinessInvoiceItemAdjustmentModelDao(final Account account,
+                                                 final Invoice invoice,
+                                                 final InvoiceItem invoiceItem,
+                                                 @Nullable final SubscriptionBundle bundle,
+                                                 @Nullable final Plan plan,
+                                                 @Nullable final PlanPhase planPhase,
+                                                 final AuditLog creationAuditLog) {
+        super(account,
+              invoice,
+              invoiceItem,
+              bundle,
+              plan,
+              planPhase,
+              creationAuditLog);
+    }
+
+    @Override
+    public String getTableName() {
+        return INVOICE_ITEM_ADJUSTMENTS_TABLE_NAME;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceItemBaseModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceItemBaseModelDao.java
new file mode 100644
index 0000000..4720ab9
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceItemBaseModelDao.java
@@ -0,0 +1,493 @@
+/*
+ * 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.osgi.bundles.analytics.model;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+
+import com.ning.billing.account.api.Account;
+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.user.SubscriptionBundle;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
+import com.ning.billing.util.audit.AuditLog;
+
+public abstract class BusinessInvoiceItemBaseModelDao extends BusinessModelDaoBase {
+
+    protected static final String INVOICE_ADJUSTMENTS_TABLE_NAME = "bia";
+    protected static final String INVOICE_ITEMS_TABLE_NAME = "bii";
+    protected static final String INVOICE_ITEM_ADJUSTMENTS_TABLE_NAME = "biia";
+    protected static final String ACCOUNT_CREDITS_TABLE_NAME = "biic";
+
+    public static final String[] ALL_INVOICE_ITEMS_TABLE_NAMES = new String[]{INVOICE_ADJUSTMENTS_TABLE_NAME, INVOICE_ITEMS_TABLE_NAME, INVOICE_ITEM_ADJUSTMENTS_TABLE_NAME, ACCOUNT_CREDITS_TABLE_NAME};
+
+    private final Long invoiceItemRecordId;
+    private final UUID itemId;
+    private final UUID invoiceId;
+    private final Integer invoiceNumber;
+    private final DateTime invoiceCreatedDate;
+    private final LocalDate invoiceDate;
+    private final LocalDate invoiceTargetDate;
+    private final String invoiceCurrency;
+    private final BigDecimal invoiceBalance;
+    private final BigDecimal invoiceAmountPaid;
+    private final BigDecimal invoiceAmountCharged;
+    private final BigDecimal invoiceOriginalAmountCharged;
+    private final BigDecimal invoiceAmountCredited;
+    private final String itemType;
+    private final Boolean recognizable;
+    private final String bundleExternalKey;
+    private final String productName;
+    private final String productType;
+    private final String productCategory;
+    private final String slug;
+    private final String phase;
+    private final String billingPeriod;
+    private final LocalDate startDate;
+    private final LocalDate endDate;
+    private final BigDecimal amount;
+    private final Currency currency;
+    private final UUID linkedItemId;
+
+    public static BusinessInvoiceItemBaseModelDao create(final Account account,
+                                                         final Invoice invoice,
+                                                         final InvoiceItem invoiceItem,
+                                                         @Nullable final SubscriptionBundle bundle,
+                                                         @Nullable final Plan plan,
+                                                         @Nullable final PlanPhase planPhase,
+                                                         final AuditLog creationAuditLog) {
+        if (InvoiceItemType.REFUND_ADJ.equals(invoiceItem.getInvoiceItemType())) {
+            return new BusinessInvoiceAdjustmentModelDao(account, invoice, invoiceItem, bundle, plan, planPhase, creationAuditLog);
+        } else if (InvoiceItemType.EXTERNAL_CHARGE.equals(invoiceItem.getInvoiceItemType()) ||
+                   InvoiceItemType.FIXED.equals(invoiceItem.getInvoiceItemType()) ||
+                   InvoiceItemType.RECURRING.equals(invoiceItem.getInvoiceItemType())) {
+            return new BusinessInvoiceItemModelDao(account, invoice, invoiceItem, bundle, plan, planPhase, creationAuditLog);
+        } else if (InvoiceItemType.ITEM_ADJ.equals(invoiceItem.getInvoiceItemType())) {
+            return new BusinessInvoiceItemAdjustmentModelDao(account, invoice, invoiceItem, bundle, plan, planPhase, creationAuditLog);
+        } else if ((InvoiceItemType.CBA_ADJ.equals(invoiceItem.getInvoiceItemType()) && invoiceItem.getAmount().compareTo(BigDecimal.ZERO) <= 0) ||
+                   InvoiceItemType.CREDIT_ADJ.equals(invoiceItem.getInvoiceItemType())) {
+            return new BusinessInvoiceItemCreditModelDao(account, invoice, invoiceItem, bundle, plan, planPhase, creationAuditLog);
+        } else {
+            // We don't care
+            return null;
+        }
+    }
+
+    private BusinessInvoiceItemBaseModelDao(final Long invoiceItemRecordId,
+                                            final UUID itemId,
+                                            final UUID invoiceId,
+                                            final Integer invoiceNumber,
+                                            final DateTime invoiceCreatedDate,
+                                            final LocalDate invoiceDate,
+                                            final LocalDate invoiceTargetDate,
+                                            final String invoiceCurrency,
+                                            final BigDecimal invoiceBalance,
+                                            final BigDecimal invoiceAmountPaid,
+                                            final BigDecimal invoiceAmountCharged,
+                                            final BigDecimal invoiceOriginalAmountCharged,
+                                            final BigDecimal invoiceAmountCredited,
+                                            final String itemType,
+                                            final Boolean recognizable,
+                                            final String bundleExternalKey,
+                                            final String productName,
+                                            final String productType,
+                                            final String productCategory,
+                                            final String slug,
+                                            final String phase,
+                                            final String billingPeriod,
+                                            final LocalDate startDate,
+                                            final LocalDate endDate,
+                                            final BigDecimal amount,
+                                            final Currency currency,
+                                            final UUID linkedItemId,
+                                            final DateTime createdDate,
+                                            final String createdBy,
+                                            final String createdReasonCode,
+                                            final String createdComments,
+                                            final UUID accountId,
+                                            final String accountName,
+                                            final String accountExternalKey,
+                                            final Long accountRecordId,
+                                            final Long tenantRecordId) {
+        super(createdDate,
+              createdBy,
+              createdReasonCode,
+              createdComments,
+              accountId,
+              accountName,
+              accountExternalKey,
+              accountRecordId,
+              tenantRecordId);
+        this.invoiceItemRecordId = invoiceItemRecordId;
+        this.itemId = itemId;
+        this.invoiceId = invoiceId;
+        this.invoiceNumber = invoiceNumber;
+        this.invoiceCreatedDate = invoiceCreatedDate;
+        this.invoiceDate = invoiceDate;
+        this.invoiceTargetDate = invoiceTargetDate;
+        this.invoiceCurrency = invoiceCurrency;
+        this.invoiceBalance = invoiceBalance;
+        this.invoiceAmountPaid = invoiceAmountPaid;
+        this.invoiceAmountCharged = invoiceAmountCharged;
+        this.invoiceOriginalAmountCharged = invoiceOriginalAmountCharged;
+        this.invoiceAmountCredited = invoiceAmountCredited;
+        this.itemType = itemType;
+        this.recognizable = recognizable;
+        this.bundleExternalKey = bundleExternalKey;
+        this.productName = productName;
+        this.productType = productType;
+        this.productCategory = productCategory;
+        this.slug = slug;
+        this.phase = phase;
+        this.billingPeriod = billingPeriod;
+        this.startDate = startDate;
+        this.endDate = endDate;
+        this.amount = amount;
+        this.currency = currency;
+        this.linkedItemId = linkedItemId;
+    }
+
+    protected BusinessInvoiceItemBaseModelDao(final Account account,
+                                              final Invoice invoice,
+                                              final InvoiceItem invoiceItem,
+                                              @Nullable final SubscriptionBundle bundle,
+                                              @Nullable final Plan plan,
+                                              @Nullable final PlanPhase planPhase,
+                                              final AuditLog creationAuditLog) {
+        this(null /* TODO */,
+             invoiceItem.getId(),
+             invoice.getId(),
+             invoice.getInvoiceNumber(),
+             invoice.getCreatedDate(),
+             invoice.getInvoiceDate(),
+             invoice.getTargetDate(),
+             invoice.getCurrency() == null ? null : invoice.getCurrency().toString(),
+             invoice.getBalance(),
+             invoice.getPaidAmount(),
+             invoice.getChargedAmount(),
+             null /* TODO */,
+             invoice.getCreditAdjAmount(),
+             invoiceItem.getInvoiceItemType().toString(),
+             null /* TODO */,
+             bundle == null ? null : bundle.getExternalKey(),
+             plan != null ? plan.getProduct().getName() : null,
+             plan != null ? plan.getProduct().getCatalogName() : null,
+             plan != null ? plan.getProduct().getCategory().toString() : null,
+             planPhase != null ? planPhase.getName() : null,
+             planPhase != null ? planPhase.getPhaseType().toString() : null,
+             planPhase != null ? planPhase.getBillingPeriod().toString() : null,
+             invoiceItem.getStartDate(),
+             /* Populate end date for fixed items for convenience (null in invoice_items table) */
+             (invoiceItem.getEndDate() == null && planPhase != null) ? invoiceItem.getStartDate().plus(planPhase.getDuration().toJodaPeriod()) : invoiceItem.getEndDate(),
+             invoiceItem.getAmount(),
+             invoiceItem.getCurrency(),
+             invoiceItem.getLinkedItemId(),
+             invoiceItem.getCreatedDate(),
+             creationAuditLog.getUserName(),
+             creationAuditLog.getReasonCode(),
+             creationAuditLog.getComment(),
+             account.getId(),
+             account.getName(),
+             account.getExternalKey(),
+             // TODO
+             null,
+             null);
+    }
+
+    public Long getInvoiceItemRecordId() {
+        return invoiceItemRecordId;
+    }
+
+    public UUID getItemId() {
+        return itemId;
+    }
+
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    public Integer getInvoiceNumber() {
+        return invoiceNumber;
+    }
+
+    public DateTime getInvoiceCreatedDate() {
+        return invoiceCreatedDate;
+    }
+
+    public LocalDate getInvoiceDate() {
+        return invoiceDate;
+    }
+
+    public LocalDate getInvoiceTargetDate() {
+        return invoiceTargetDate;
+    }
+
+    public String getInvoiceCurrency() {
+        return invoiceCurrency;
+    }
+
+    public BigDecimal getInvoiceBalance() {
+        return invoiceBalance;
+    }
+
+    public BigDecimal getInvoiceAmountPaid() {
+        return invoiceAmountPaid;
+    }
+
+    public BigDecimal getInvoiceAmountCharged() {
+        return invoiceAmountCharged;
+    }
+
+    public BigDecimal getInvoiceOriginalAmountCharged() {
+        return invoiceOriginalAmountCharged;
+    }
+
+    public BigDecimal getInvoiceAmountCredited() {
+        return invoiceAmountCredited;
+    }
+
+    public String getItemType() {
+        return itemType;
+    }
+
+    public Boolean getRecognizable() {
+        return recognizable;
+    }
+
+    public String getBundleExternalKey() {
+        return bundleExternalKey;
+    }
+
+    public String getProductName() {
+        return productName;
+    }
+
+    public String getProductType() {
+        return productType;
+    }
+
+    public String getProductCategory() {
+        return productCategory;
+    }
+
+    public String getSlug() {
+        return slug;
+    }
+
+    public String getPhase() {
+        return phase;
+    }
+
+    public String getBillingPeriod() {
+        return billingPeriod;
+    }
+
+    public LocalDate getStartDate() {
+        return startDate;
+    }
+
+    public LocalDate getEndDate() {
+        return endDate;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    public UUID getLinkedItemId() {
+        return linkedItemId;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessInvoiceItemBaseModelDao");
+        sb.append("{invoiceItemRecordId=").append(invoiceItemRecordId);
+        sb.append(", itemId=").append(itemId);
+        sb.append(", invoiceId=").append(invoiceId);
+        sb.append(", invoiceNumber=").append(invoiceNumber);
+        sb.append(", invoiceCreatedDate=").append(invoiceCreatedDate);
+        sb.append(", invoiceDate=").append(invoiceDate);
+        sb.append(", invoiceTargetDate=").append(invoiceTargetDate);
+        sb.append(", invoiceCurrency='").append(invoiceCurrency).append('\'');
+        sb.append(", invoiceBalance=").append(invoiceBalance);
+        sb.append(", invoiceAmountPaid=").append(invoiceAmountPaid);
+        sb.append(", invoiceAmountCharged=").append(invoiceAmountCharged);
+        sb.append(", invoiceOriginalAmountCharged=").append(invoiceOriginalAmountCharged);
+        sb.append(", invoiceAmountCredited=").append(invoiceAmountCredited);
+        sb.append(", itemType='").append(itemType).append('\'');
+        sb.append(", recognizable=").append(recognizable);
+        sb.append(", bundleExternalKey='").append(bundleExternalKey).append('\'');
+        sb.append(", productName='").append(productName).append('\'');
+        sb.append(", productType='").append(productType).append('\'');
+        sb.append(", productCategory='").append(productCategory).append('\'');
+        sb.append(", slug='").append(slug).append('\'');
+        sb.append(", phase='").append(phase).append('\'');
+        sb.append(", billingPeriod='").append(billingPeriod).append('\'');
+        sb.append(", startDate=").append(startDate);
+        sb.append(", endDate=").append(endDate);
+        sb.append(", amount=").append(amount);
+        sb.append(", currency=").append(currency);
+        sb.append(", linkedItemId=").append(linkedItemId);
+        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 BusinessInvoiceItemBaseModelDao that = (BusinessInvoiceItemBaseModelDao) o;
+
+        if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
+            return false;
+        }
+        if (billingPeriod != null ? !billingPeriod.equals(that.billingPeriod) : that.billingPeriod != null) {
+            return false;
+        }
+        if (bundleExternalKey != null ? !bundleExternalKey.equals(that.bundleExternalKey) : that.bundleExternalKey != null) {
+            return false;
+        }
+        if (currency != that.currency) {
+            return false;
+        }
+        if (endDate != null ? !endDate.equals(that.endDate) : that.endDate != null) {
+            return false;
+        }
+        if (invoiceAmountCharged != null ? !invoiceAmountCharged.equals(that.invoiceAmountCharged) : that.invoiceAmountCharged != null) {
+            return false;
+        }
+        if (invoiceAmountCredited != null ? !invoiceAmountCredited.equals(that.invoiceAmountCredited) : that.invoiceAmountCredited != null) {
+            return false;
+        }
+        if (invoiceAmountPaid != null ? !invoiceAmountPaid.equals(that.invoiceAmountPaid) : that.invoiceAmountPaid != null) {
+            return false;
+        }
+        if (invoiceBalance != null ? !invoiceBalance.equals(that.invoiceBalance) : that.invoiceBalance != null) {
+            return false;
+        }
+        if (invoiceCreatedDate != null ? !invoiceCreatedDate.equals(that.invoiceCreatedDate) : that.invoiceCreatedDate != null) {
+            return false;
+        }
+        if (invoiceCurrency != null ? !invoiceCurrency.equals(that.invoiceCurrency) : that.invoiceCurrency != null) {
+            return false;
+        }
+        if (invoiceDate != null ? !invoiceDate.equals(that.invoiceDate) : that.invoiceDate != null) {
+            return false;
+        }
+        if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) {
+            return false;
+        }
+        if (invoiceItemRecordId != null ? !invoiceItemRecordId.equals(that.invoiceItemRecordId) : that.invoiceItemRecordId != null) {
+            return false;
+        }
+        if (invoiceNumber != null ? !invoiceNumber.equals(that.invoiceNumber) : that.invoiceNumber != null) {
+            return false;
+        }
+        if (invoiceOriginalAmountCharged != null ? !invoiceOriginalAmountCharged.equals(that.invoiceOriginalAmountCharged) : that.invoiceOriginalAmountCharged != null) {
+            return false;
+        }
+        if (invoiceTargetDate != null ? !invoiceTargetDate.equals(that.invoiceTargetDate) : that.invoiceTargetDate != null) {
+            return false;
+        }
+        if (itemId != null ? !itemId.equals(that.itemId) : that.itemId != null) {
+            return false;
+        }
+        if (itemType != null ? !itemType.equals(that.itemType) : that.itemType != null) {
+            return false;
+        }
+        if (linkedItemId != null ? !linkedItemId.equals(that.linkedItemId) : that.linkedItemId != null) {
+            return false;
+        }
+        if (phase != null ? !phase.equals(that.phase) : that.phase != null) {
+            return false;
+        }
+        if (productCategory != null ? !productCategory.equals(that.productCategory) : that.productCategory != null) {
+            return false;
+        }
+        if (productName != null ? !productName.equals(that.productName) : that.productName != null) {
+            return false;
+        }
+        if (productType != null ? !productType.equals(that.productType) : that.productType != null) {
+            return false;
+        }
+        if (recognizable != null ? !recognizable.equals(that.recognizable) : that.recognizable != null) {
+            return false;
+        }
+        if (slug != null ? !slug.equals(that.slug) : that.slug != null) {
+            return false;
+        }
+        if (startDate != null ? !startDate.equals(that.startDate) : that.startDate != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (invoiceItemRecordId != null ? invoiceItemRecordId.hashCode() : 0);
+        result = 31 * result + (itemId != null ? itemId.hashCode() : 0);
+        result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
+        result = 31 * result + (invoiceNumber != null ? invoiceNumber.hashCode() : 0);
+        result = 31 * result + (invoiceCreatedDate != null ? invoiceCreatedDate.hashCode() : 0);
+        result = 31 * result + (invoiceDate != null ? invoiceDate.hashCode() : 0);
+        result = 31 * result + (invoiceTargetDate != null ? invoiceTargetDate.hashCode() : 0);
+        result = 31 * result + (invoiceCurrency != null ? invoiceCurrency.hashCode() : 0);
+        result = 31 * result + (invoiceBalance != null ? invoiceBalance.hashCode() : 0);
+        result = 31 * result + (invoiceAmountPaid != null ? invoiceAmountPaid.hashCode() : 0);
+        result = 31 * result + (invoiceAmountCharged != null ? invoiceAmountCharged.hashCode() : 0);
+        result = 31 * result + (invoiceOriginalAmountCharged != null ? invoiceOriginalAmountCharged.hashCode() : 0);
+        result = 31 * result + (invoiceAmountCredited != null ? invoiceAmountCredited.hashCode() : 0);
+        result = 31 * result + (itemType != null ? itemType.hashCode() : 0);
+        result = 31 * result + (recognizable != null ? recognizable.hashCode() : 0);
+        result = 31 * result + (bundleExternalKey != null ? bundleExternalKey.hashCode() : 0);
+        result = 31 * result + (productName != null ? productName.hashCode() : 0);
+        result = 31 * result + (productType != null ? productType.hashCode() : 0);
+        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 + (startDate != null ? startDate.hashCode() : 0);
+        result = 31 * result + (endDate != null ? endDate.hashCode() : 0);
+        result = 31 * result + (amount != null ? amount.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (linkedItemId != null ? linkedItemId.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceItemCreditModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceItemCreditModelDao.java
new file mode 100644
index 0000000..89e2788
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceItemCreditModelDao.java
@@ -0,0 +1,51 @@
+/*
+ * 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.osgi.bundles.analytics.model;
+
+import javax.annotation.Nullable;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.util.audit.AuditLog;
+
+public class BusinessInvoiceItemCreditModelDao extends BusinessInvoiceItemBaseModelDao {
+
+    public BusinessInvoiceItemCreditModelDao(final Account account,
+                                             final Invoice invoice,
+                                             final InvoiceItem invoiceItem,
+                                             @Nullable final SubscriptionBundle bundle,
+                                             @Nullable final Plan plan,
+                                             @Nullable final PlanPhase planPhase,
+                                             final AuditLog creationAuditLogs) {
+        super(account,
+              invoice,
+              invoiceItem,
+              bundle,
+              plan,
+              planPhase,
+              creationAuditLogs);
+    }
+
+    @Override
+    public String getTableName() {
+        return ACCOUNT_CREDITS_TABLE_NAME;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceItemModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceItemModelDao.java
index 53f58e6..293428a 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceItemModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceItemModelDao.java
@@ -16,242 +16,36 @@
 
 package com.ning.billing.osgi.bundles.analytics.model;
 
-import java.math.BigDecimal;
-import java.util.UUID;
-
 import javax.annotation.Nullable;
 
-import org.joda.time.DateTime;
-import org.joda.time.LocalDate;
-
-import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.account.api.Account;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.osgi.bundles.analytics.utils.Rounder;
-import com.ning.billing.util.entity.EntityBase;
-
-public class BusinessInvoiceItemModelDao extends EntityBase {
-
-    private final UUID itemId;
-    private final UUID invoiceId;
-    private final String itemType;
-    private final String externalKey;
-    private final String productName;
-    private final String productType;
-    private final String productCategory;
-    private final String slug;
-    private final String phase;
-    private final String billingPeriod;
-    private final LocalDate startDate;
-    private final LocalDate endDate;
-    private final BigDecimal amount;
-    private final Currency currency;
-    private final UUID linkedItemId;
-
-    public BusinessInvoiceItemModelDao(final BigDecimal amount, @Nullable final String billingPeriod, final DateTime createdDate,
-                                       final Currency currency, final LocalDate endDate, final String externalKey,
-                                       final UUID invoiceId, final UUID itemId, @Nullable final UUID linkedItemId, final String itemType,
-                                       @Nullable final String phase, @Nullable final String productCategory, @Nullable final String productName,
-                                       @Nullable final String productType, @Nullable final String slug, final LocalDate startDate, final DateTime updatedDate) {
-        super(itemId, createdDate, updatedDate);
-        this.amount = amount;
-        this.billingPeriod = billingPeriod;
-        this.currency = currency;
-        this.endDate = endDate;
-        this.externalKey = externalKey;
-        this.invoiceId = invoiceId;
-        this.itemId = itemId;
-        this.linkedItemId = linkedItemId;
-        this.itemType = itemType;
-        this.phase = phase;
-        this.productCategory = productCategory;
-        this.productName = productName;
-        this.productType = productType;
-        this.slug = slug;
-        this.startDate = startDate;
-    }
-
-    public BusinessInvoiceItemModelDao(@Nullable final String externalKey, final InvoiceItem invoiceItem, @Nullable final Plan plan, @Nullable final PlanPhase planPhase) {
-        this(invoiceItem.getAmount(), planPhase != null ? planPhase.getBillingPeriod().toString() : null, invoiceItem.getCreatedDate(), invoiceItem.getCurrency(),
-             /* Populate end date for fixed items for convenience (null in invoice_items table) */
-             (invoiceItem.getEndDate() == null && planPhase != null) ? invoiceItem.getStartDate().plus(planPhase.getDuration().toJodaPeriod()) : invoiceItem.getEndDate(),
-             externalKey, invoiceItem.getInvoiceId(), invoiceItem.getId(), invoiceItem.getLinkedItemId(), invoiceItem.getInvoiceItemType().toString(),
-             planPhase != null ? planPhase.getPhaseType().toString() : null, plan != null ? plan.getProduct().getCategory().toString() : null,
-             plan != null ? plan.getProduct().getName() : null, plan != null ? plan.getProduct().getCatalogName() : null,
-             planPhase != null ? planPhase.getName() : null, invoiceItem.getStartDate(), invoiceItem.getUpdatedDate());
-    }
-
-    public UUID getItemId() {
-        return itemId;
-    }
-
-    public BigDecimal getAmount() {
-        return amount;
-    }
-
-    public String getBillingPeriod() {
-        return billingPeriod;
-    }
-
-    public Currency getCurrency() {
-        return currency;
-    }
-
-    public LocalDate getEndDate() {
-        return endDate;
-    }
-
-    public String getExternalKey() {
-        return externalKey;
-    }
-
-    public UUID getInvoiceId() {
-        return invoiceId;
-    }
-
-    public String getItemType() {
-        return itemType;
-    }
-
-    public UUID getLinkedItemId() {
-        return linkedItemId;
-    }
-
-    public String getPhase() {
-        return phase;
-    }
-
-    public String getProductCategory() {
-        return productCategory;
-    }
-
-    public String getProductName() {
-        return productName;
-    }
-
-    public String getProductType() {
-        return productType;
-    }
-
-    public String getSlug() {
-        return slug;
-    }
-
-    public LocalDate getStartDate() {
-        return startDate;
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("BusinessInvoiceItemModelDao");
-        sb.append("{amount=").append(amount);
-        sb.append(", itemId=").append(itemId);
-        sb.append(", createdDate=").append(createdDate);
-        sb.append(", updatedDate=").append(updatedDate);
-        sb.append(", invoiceId=").append(invoiceId);
-        sb.append(", itemType='").append(itemType).append('\'');
-        sb.append(", externalKey='").append(externalKey).append('\'');
-        sb.append(", productName='").append(productName).append('\'');
-        sb.append(", productType='").append(productType).append('\'');
-        sb.append(", productCategory='").append(productCategory).append('\'');
-        sb.append(", slug='").append(slug).append('\'');
-        sb.append(", phase='").append(phase).append('\'');
-        sb.append(", billingPeriod='").append(billingPeriod).append('\'');
-        sb.append(", startDate=").append(startDate);
-        sb.append(", endDate=").append(endDate);
-        sb.append(", currency=").append(currency);
-        sb.append(", linkedItemId=").append(linkedItemId);
-        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 BusinessInvoiceItemModelDao that = (BusinessInvoiceItemModelDao) o;
-
-        if (amount != null ? Rounder.round(amount) != (Rounder.round(that.amount)) : that.amount != null) {
-            return false;
-        }
-        if (billingPeriod != null ? !billingPeriod.equals(that.billingPeriod) : that.billingPeriod != null) {
-            return false;
-        }
-        if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
-            return false;
-        }
-        if (currency != that.currency) {
-            return false;
-        }
-        if (endDate != null ? !endDate.equals(that.endDate) : that.endDate != null) {
-            return false;
-        }
-        if (externalKey != null ? !externalKey.equals(that.externalKey) : that.externalKey != null) {
-            return false;
-        }
-        if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) {
-            return false;
-        }
-        if (itemId != null ? !itemId.equals(that.itemId) : that.itemId != null) {
-            return false;
-        }
-        if (itemType != null ? !itemType.equals(that.itemType) : that.itemType != null) {
-            return false;
-        }
-        if (linkedItemId != null ? !linkedItemId.equals(that.linkedItemId) : that.linkedItemId != null) {
-            return false;
-        }
-        if (phase != null ? !phase.equals(that.phase) : that.phase != null) {
-            return false;
-        }
-        if (productCategory != null ? !productCategory.equals(that.productCategory) : that.productCategory != null) {
-            return false;
-        }
-        if (productName != null ? !productName.equals(that.productName) : that.productName != null) {
-            return false;
-        }
-        if (productType != null ? !productType.equals(that.productType) : that.productType != null) {
-            return false;
-        }
-        if (slug != null ? !slug.equals(that.slug) : that.slug != null) {
-            return false;
-        }
-        if (startDate != null ? !startDate.equals(that.startDate) : that.startDate != null) {
-            return false;
-        }
-        if (updatedDate != null ? !updatedDate.equals(that.updatedDate) : that.updatedDate != null) {
-            return false;
-        }
-
-        return true;
+import com.ning.billing.util.audit.AuditLog;
+
+public class BusinessInvoiceItemModelDao extends BusinessInvoiceItemBaseModelDao {
+
+    public BusinessInvoiceItemModelDao(final Account account,
+                                       final Invoice invoice,
+                                       final InvoiceItem invoiceItem,
+                                       @Nullable final SubscriptionBundle bundle,
+                                       @Nullable final Plan plan,
+                                       @Nullable final PlanPhase planPhase,
+                                       final AuditLog creationAuditLog) {
+        super(account,
+              invoice,
+              invoiceItem,
+              bundle,
+              plan,
+              planPhase,
+              creationAuditLog);
     }
 
     @Override
-    public int hashCode() {
-        int result = itemId != null ? itemId.hashCode() : 0;
-        result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
-        result = 31 * result + (updatedDate != null ? updatedDate.hashCode() : 0);
-        result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
-        result = 31 * result + (itemType != null ? itemType.hashCode() : 0);
-        result = 31 * result + (externalKey != null ? externalKey.hashCode() : 0);
-        result = 31 * result + (productName != null ? productName.hashCode() : 0);
-        result = 31 * result + (productType != null ? productType.hashCode() : 0);
-        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 + (startDate != null ? startDate.hashCode() : 0);
-        result = 31 * result + (endDate != null ? endDate.hashCode() : 0);
-        result = 31 * result + (amount != null ? amount.hashCode() : 0);
-        result = 31 * result + (currency != null ? currency.hashCode() : 0);
-        result = 31 * result + (linkedItemId != null ? linkedItemId.hashCode() : 0);
-        return result;
+    public String getTableName() {
+        return INVOICE_ITEMS_TABLE_NAME;
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceModelDao.java
index 4515283..df13b1e 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceModelDao.java
@@ -22,109 +22,157 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 
-import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.account.api.Account;
 import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.osgi.bundles.analytics.utils.Rounder;
-import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.util.audit.AuditLog;
 
-public class BusinessInvoiceModelDao extends EntityBase {
+public class BusinessInvoiceModelDao extends BusinessModelDaoBase {
 
+    public static final String INVOICES_TABLE_NAME = "bin";
+
+    private final Long invoiceRecordId;
     private final UUID invoiceId;
     private final Integer invoiceNumber;
-    private final UUID accountId;
-    private final String accountKey;
     private final LocalDate invoiceDate;
     private final LocalDate targetDate;
-    private final Currency currency;
+    private final String currency;
     private final BigDecimal balance;
     private final BigDecimal amountPaid;
     private final BigDecimal amountCharged;
+    private final BigDecimal originalAmountCharged;
     private final BigDecimal amountCredited;
 
-    public BusinessInvoiceModelDao(final UUID accountId, final String accountKey, final BigDecimal amountCharged, final BigDecimal amountCredited,
-                                   final BigDecimal amountPaid, final BigDecimal balance, final DateTime createdDate,
-                                   final Currency currency, final LocalDate invoiceDate, final UUID invoiceId, final Integer invoiceNumber,
-                                   final LocalDate targetDate, final DateTime updatedDate) {
-        super(invoiceId, createdDate, updatedDate);
-        this.accountId = accountId;
-        this.accountKey = accountKey;
-        this.amountCharged = amountCharged;
-        this.amountCredited = amountCredited;
-        this.amountPaid = amountPaid;
-        this.balance = balance;
-        this.currency = currency;
-        this.invoiceDate = invoiceDate;
+    public BusinessInvoiceModelDao(final Long invoiceRecordId,
+                                   final UUID invoiceId,
+                                   final Integer invoiceNumber,
+                                   final LocalDate invoiceDate,
+                                   final LocalDate targetDate,
+                                   final String currency,
+                                   final BigDecimal balance,
+                                   final BigDecimal amountPaid,
+                                   final BigDecimal amountCharged,
+                                   final BigDecimal originalAmountCharged,
+                                   final BigDecimal amountCredited,
+                                   final DateTime createdDate,
+                                   final String createdBy,
+                                   final String createdReasonCode,
+                                   final String createdComments,
+                                   final UUID accountId,
+                                   final String accountName,
+                                   final String accountExternalKey,
+                                   final Long accountRecordId,
+                                   final Long tenantRecordId) {
+        super(createdDate,
+              createdBy,
+              createdReasonCode,
+              createdComments,
+              accountId,
+              accountName,
+              accountExternalKey,
+              accountRecordId,
+              tenantRecordId);
+        this.invoiceRecordId = invoiceRecordId;
         this.invoiceId = invoiceId;
         this.invoiceNumber = invoiceNumber;
+        this.invoiceDate = invoiceDate;
         this.targetDate = targetDate;
+        this.currency = currency;
+        this.balance = balance;
+        this.amountPaid = amountPaid;
+        this.amountCharged = amountCharged;
+        this.originalAmountCharged = originalAmountCharged;
+        this.amountCredited = amountCredited;
     }
 
-    public BusinessInvoiceModelDao(final String accountKey, final Invoice invoice) {
-        this(invoice.getAccountId(), accountKey, invoice.getChargedAmount(), invoice.getCreditAdjAmount(), invoice.getPaidAmount(), invoice.getBalance(),
-             invoice.getCreatedDate(), invoice.getCurrency(), invoice.getInvoiceDate(), invoice.getId(), invoice.getInvoiceNumber(), invoice.getTargetDate(),
-             invoice.getUpdatedDate());
+    public BusinessInvoiceModelDao(final Account account,
+                                   final Invoice invoice,
+                                   final AuditLog creationAuditLog) {
+        this(null /* TODO */,
+             invoice.getId(),
+             invoice.getInvoiceNumber(),
+             invoice.getInvoiceDate(),
+             invoice.getTargetDate(),
+             invoice.getCurrency() == null ? null : invoice.getCurrency().toString(),
+             invoice.getBalance(),
+             invoice.getPaidAmount(),
+             invoice.getChargedAmount(),
+             null /* TODO */,
+             invoice.getCreditAdjAmount(),
+             invoice.getCreatedDate(),
+             creationAuditLog.getUserName(),
+             creationAuditLog.getReasonCode(),
+             creationAuditLog.getComment(),
+             account.getId(),
+             account.getName(),
+             account.getExternalKey(),
+             // TODO
+             null,
+             null);
     }
 
-    public UUID getAccountId() {
-        return accountId;
+    @Override
+    public String getTableName() {
+        return INVOICES_TABLE_NAME;
     }
 
-    public String getAccountKey() {
-        return accountKey;
+    public Long getInvoiceRecordId() {
+        return invoiceRecordId;
     }
 
-    public BigDecimal getAmountCharged() {
-        return amountCharged;
+    public UUID getInvoiceId() {
+        return invoiceId;
     }
 
-    public BigDecimal getAmountCredited() {
-        return amountCredited;
+    public Integer getInvoiceNumber() {
+        return invoiceNumber;
     }
 
-    public BigDecimal getAmountPaid() {
-        return amountPaid;
+    public LocalDate getInvoiceDate() {
+        return invoiceDate;
     }
 
-    public BigDecimal getBalance() {
-        return balance;
+    public LocalDate getTargetDate() {
+        return targetDate;
     }
 
-    public Currency getCurrency() {
+    public String getCurrency() {
         return currency;
     }
 
-    public LocalDate getInvoiceDate() {
-        return invoiceDate;
+    public BigDecimal getBalance() {
+        return balance;
     }
 
-    public UUID getInvoiceId() {
-        return invoiceId;
+    public BigDecimal getAmountPaid() {
+        return amountPaid;
     }
 
-    public Integer getInvoiceNumber() {
-        return invoiceNumber;
+    public BigDecimal getAmountCharged() {
+        return amountCharged;
     }
 
-    public LocalDate getTargetDate() {
-        return targetDate;
+    public BigDecimal getOriginalAmountCharged() {
+        return originalAmountCharged;
+    }
+
+    public BigDecimal getAmountCredited() {
+        return amountCredited;
     }
 
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         sb.append("BusinessInvoiceModelDao");
-        sb.append("{accountId=").append(accountId);
+        sb.append("{invoiceRecordId=").append(invoiceRecordId);
         sb.append(", invoiceId=").append(invoiceId);
         sb.append(", invoiceNumber=").append(invoiceNumber);
-        sb.append(", createdDate=").append(createdDate);
-        sb.append(", updatedDate=").append(updatedDate);
-        sb.append(", accountKey='").append(accountKey).append('\'');
         sb.append(", invoiceDate=").append(invoiceDate);
         sb.append(", targetDate=").append(targetDate);
-        sb.append(", currency=").append(currency);
+        sb.append(", currency='").append(currency).append('\'');
         sb.append(", balance=").append(balance);
         sb.append(", amountPaid=").append(amountPaid);
         sb.append(", amountCharged=").append(amountCharged);
+        sb.append(", originalAmountCharged=").append(originalAmountCharged);
         sb.append(", amountCredited=").append(amountCredited);
         sb.append('}');
         return sb.toString();
@@ -138,34 +186,28 @@ public class BusinessInvoiceModelDao extends EntityBase {
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
+        if (!super.equals(o)) {
+            return false;
+        }
 
         final BusinessInvoiceModelDao that = (BusinessInvoiceModelDao) o;
 
-        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 (amountCharged != null ? Rounder.round(amountCharged) != Rounder.round(that.amountCharged) : that.amountCharged != null) {
+        if (amountCharged != null ? !amountCharged.equals(that.amountCharged) : that.amountCharged != null) {
             return false;
         }
-        if (amountCredited != null ? Rounder.round(amountCredited) != Rounder.round(that.amountCredited) : that.amountCredited != null) {
+        if (amountCredited != null ? !amountCredited.equals(that.amountCredited) : that.amountCredited != null) {
             return false;
         }
-        if (amountPaid != null ? Rounder.round(amountPaid) != Rounder.round(that.amountPaid) : that.amountPaid != null) {
+        if (amountPaid != null ? !amountPaid.equals(that.amountPaid) : that.amountPaid != null) {
             return false;
         }
-        if (balance != null ? Rounder.round(balance) != Rounder.round(that.balance) : that.balance != null) {
+        if (balance != null ? !balance.equals(that.balance) : that.balance != null) {
             return false;
         }
-        if (createdDate != null ? createdDate.compareTo(that.createdDate) != 0 : that.createdDate != null) {
+        if (currency != null ? !currency.equals(that.currency) : that.currency != null) {
             return false;
         }
-        if (currency != that.currency) {
-            return false;
-        }
-        if (invoiceDate != null ? invoiceDate.compareTo(that.invoiceDate) != 0 : that.invoiceDate != null) {
+        if (invoiceDate != null ? !invoiceDate.equals(that.invoiceDate) : that.invoiceDate != null) {
             return false;
         }
         if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) {
@@ -174,10 +216,13 @@ public class BusinessInvoiceModelDao extends EntityBase {
         if (invoiceNumber != null ? !invoiceNumber.equals(that.invoiceNumber) : that.invoiceNumber != null) {
             return false;
         }
-        if (targetDate != null ? targetDate.compareTo(that.targetDate) != 0 : that.targetDate != null) {
+        if (invoiceRecordId != null ? !invoiceRecordId.equals(that.invoiceRecordId) : that.invoiceRecordId != null) {
+            return false;
+        }
+        if (originalAmountCharged != null ? !originalAmountCharged.equals(that.originalAmountCharged) : that.originalAmountCharged != null) {
             return false;
         }
-        if (updatedDate != null ? updatedDate.compareTo(that.updatedDate) != 0 : that.updatedDate != null) {
+        if (targetDate != null ? !targetDate.equals(that.targetDate) : that.targetDate != null) {
             return false;
         }
 
@@ -186,18 +231,17 @@ public class BusinessInvoiceModelDao extends EntityBase {
 
     @Override
     public int hashCode() {
-        int result = invoiceId != null ? invoiceId.hashCode() : 0;
+        int result = super.hashCode();
+        result = 31 * result + (invoiceRecordId != null ? invoiceRecordId.hashCode() : 0);
+        result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
         result = 31 * result + (invoiceNumber != null ? invoiceNumber.hashCode() : 0);
-        result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
-        result = 31 * result + (updatedDate != null ? updatedDate.hashCode() : 0);
-        result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
-        result = 31 * result + (accountKey != null ? accountKey.hashCode() : 0);
         result = 31 * result + (invoiceDate != null ? invoiceDate.hashCode() : 0);
         result = 31 * result + (targetDate != null ? targetDate.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
         result = 31 * result + (balance != null ? balance.hashCode() : 0);
         result = 31 * result + (amountPaid != null ? amountPaid.hashCode() : 0);
         result = 31 * result + (amountCharged != null ? amountCharged.hashCode() : 0);
+        result = 31 * result + (originalAmountCharged != null ? originalAmountCharged.hashCode() : 0);
         result = 31 * result + (amountCredited != null ? amountCredited.hashCode() : 0);
         return result;
     }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentBaseModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentBaseModelDao.java
new file mode 100644
index 0000000..b3616fc
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentBaseModelDao.java
@@ -0,0 +1,361 @@
+/*
+ * 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.osgi.bundles.analytics.model;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.invoice.api.InvoicePayment.InvoicePaymentType;
+import com.ning.billing.payment.api.Payment;
+import com.ning.billing.payment.api.PaymentMethod;
+import com.ning.billing.util.audit.AuditLog;
+
+public abstract class BusinessInvoicePaymentBaseModelDao extends BusinessModelDaoBase {
+
+    protected static final String INVOICE_PAYMENTS_TABLE_NAME = "bip";
+    protected static final String INVOICE_PAYMENT_REFUNDS_TABLE_NAME = "bipr";
+    protected static final String INVOICE_PAYMENT_CHARGEBACKS_TABLE_NAME = "bipc";
+
+    public static final String[] ALL_INVOICE_PAYMENTS_TABLE_NAMES = new String[]{INVOICE_PAYMENTS_TABLE_NAME, INVOICE_PAYMENT_REFUNDS_TABLE_NAME, INVOICE_PAYMENT_CHARGEBACKS_TABLE_NAME};
+
+    private final Long invoicePaymentRecordId;
+    private final UUID invoicePaymentId;
+    private final UUID invoiceId;
+    private final Integer invoiceNumber;
+    private final DateTime invoiceCreatedDate;
+    private final LocalDate invoiceDate;
+    private final LocalDate invoiceTargetDate;
+    private final String invoiceCurrency;
+    private final BigDecimal invoiceBalance;
+    private final BigDecimal invoiceAmountPaid;
+    private final BigDecimal invoiceAmountCharged;
+    private final BigDecimal invoiceOriginalAmountCharged;
+    private final BigDecimal invoiceAmountCredited;
+    private final String invoicePaymentType;
+    private final Long paymentNumber;
+    private final UUID linkedInvoicePaymentId;
+    private final BigDecimal amount;
+    private final Currency currency;
+
+    public static BusinessInvoicePaymentBaseModelDao create(final Account account,
+                                                            final Invoice invoice,
+                                                            final InvoicePayment invoicePayment,
+                                                            final Payment payment,
+                                                            final PaymentMethod paymentMethod,
+                                                            final AuditLog creationAuditLog) {
+        if (invoicePayment.getType().equals(InvoicePaymentType.REFUND)) {
+            return new BusinessInvoicePaymentRefundModelDao(account, invoice, invoicePayment, payment, paymentMethod, creationAuditLog);
+        } else if (invoicePayment.getType().equals(InvoicePaymentType.CHARGED_BACK)) {
+            return new BusinessInvoicePaymentChargebackModelDao(account, invoice, invoicePayment, payment, paymentMethod, creationAuditLog);
+        } else {
+            return new BusinessInvoicePaymentModelDao(account, invoice, invoicePayment, payment, paymentMethod, creationAuditLog);
+        }
+    }
+
+    private BusinessInvoicePaymentBaseModelDao(final Long invoicePaymentRecordId,
+                                               final UUID invoicePaymentId,
+                                               final UUID invoiceId,
+                                               final Integer invoiceNumber,
+                                               final DateTime invoiceCreatedDate,
+                                               final LocalDate invoiceDate,
+                                               final LocalDate invoiceTargetDate,
+                                               final String invoiceCurrency,
+                                               final BigDecimal invoiceBalance,
+                                               final BigDecimal invoiceAmountPaid,
+                                               final BigDecimal invoiceAmountCharged,
+                                               final BigDecimal invoiceOriginalAmountCharged,
+                                               final BigDecimal invoiceAmountCredited,
+                                               final String invoicePaymentType,
+                                               final Long paymentNumber,
+                                               final UUID linkedInvoicePaymentId,
+                                               final BigDecimal amount,
+                                               final Currency currency,
+                                               final DateTime createdDate,
+                                               final String createdBy,
+                                               final String createdReasonCode,
+                                               final String createdComments,
+                                               final UUID accountId,
+                                               final String accountName,
+                                               final String accountExternalKey,
+                                               final Long accountRecordId,
+                                               final Long tenantRecordId) {
+        super(createdDate,
+              createdBy,
+              createdReasonCode,
+              createdComments,
+              accountId,
+              accountName,
+              accountExternalKey,
+              accountRecordId,
+              tenantRecordId);
+        this.invoicePaymentRecordId = invoicePaymentRecordId;
+        this.invoicePaymentId = invoicePaymentId;
+        this.invoiceId = invoiceId;
+        this.invoiceNumber = invoiceNumber;
+        this.invoiceCreatedDate = invoiceCreatedDate;
+        this.invoiceDate = invoiceDate;
+        this.invoiceTargetDate = invoiceTargetDate;
+        this.invoiceCurrency = invoiceCurrency;
+        this.invoiceBalance = invoiceBalance;
+        this.invoiceAmountPaid = invoiceAmountPaid;
+        this.invoiceAmountCharged = invoiceAmountCharged;
+        this.invoiceOriginalAmountCharged = invoiceOriginalAmountCharged;
+        this.invoiceAmountCredited = invoiceAmountCredited;
+        this.invoicePaymentType = invoicePaymentType;
+        this.paymentNumber = paymentNumber;
+        this.linkedInvoicePaymentId = linkedInvoicePaymentId;
+        this.amount = amount;
+        this.currency = currency;
+    }
+
+    protected BusinessInvoicePaymentBaseModelDao(final Account account,
+                                                 final Invoice invoice,
+                                                 final InvoicePayment invoicePayment,
+                                                 final Payment payment,
+                                                 final PaymentMethod paymentMethod,
+                                                 final AuditLog creationAuditLog) {
+        this(null /* TODO */,
+             invoicePayment.getId(),
+             invoice.getId(),
+             invoice.getInvoiceNumber(),
+             invoice.getCreatedDate(),
+             invoice.getInvoiceDate(),
+             invoice.getTargetDate(),
+             invoice.getCurrency() == null ? null : invoice.getCurrency().toString(),
+             invoice.getBalance(),
+             invoice.getPaidAmount(),
+             invoice.getChargedAmount(),
+             null /* TODO */,
+             invoice.getCreditAdjAmount(),
+             invoicePayment.getType().toString(),
+             null /* TODO */,
+             invoicePayment.getLinkedInvoicePaymentId(),
+             invoicePayment.getAmount(),
+             invoicePayment.getCurrency(),
+             invoicePayment.getCreatedDate(),
+             creationAuditLog.getUserName(),
+             creationAuditLog.getReasonCode(),
+             creationAuditLog.getComment(),
+             account.getId(),
+             account.getName(),
+             account.getExternalKey(),
+             // TODO
+             null,
+             null);
+    }
+
+    public Long getInvoicePaymentRecordId() {
+        return invoicePaymentRecordId;
+    }
+
+    public UUID getInvoicePaymentId() {
+        return invoicePaymentId;
+    }
+
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    public Integer getInvoiceNumber() {
+        return invoiceNumber;
+    }
+
+    public DateTime getInvoiceCreatedDate() {
+        return invoiceCreatedDate;
+    }
+
+    public LocalDate getInvoiceDate() {
+        return invoiceDate;
+    }
+
+    public LocalDate getInvoiceTargetDate() {
+        return invoiceTargetDate;
+    }
+
+    public String getInvoiceCurrency() {
+        return invoiceCurrency;
+    }
+
+    public BigDecimal getInvoiceBalance() {
+        return invoiceBalance;
+    }
+
+    public BigDecimal getInvoiceAmountPaid() {
+        return invoiceAmountPaid;
+    }
+
+    public BigDecimal getInvoiceAmountCharged() {
+        return invoiceAmountCharged;
+    }
+
+    public BigDecimal getInvoiceOriginalAmountCharged() {
+        return invoiceOriginalAmountCharged;
+    }
+
+    public BigDecimal getInvoiceAmountCredited() {
+        return invoiceAmountCredited;
+    }
+
+    public String getInvoicePaymentType() {
+        return invoicePaymentType;
+    }
+
+    public Long getPaymentNumber() {
+        return paymentNumber;
+    }
+
+    public UUID getLinkedInvoicePaymentId() {
+        return linkedInvoicePaymentId;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessInvoicePaymentBaseModelDao");
+        sb.append("{invoicePaymentRecordId=").append(invoicePaymentRecordId);
+        sb.append(", invoicePaymentId=").append(invoicePaymentId);
+        sb.append(", invoiceId=").append(invoiceId);
+        sb.append(", invoiceNumber=").append(invoiceNumber);
+        sb.append(", invoiceCreatedDate=").append(invoiceCreatedDate);
+        sb.append(", invoiceDate=").append(invoiceDate);
+        sb.append(", invoiceTargetDate=").append(invoiceTargetDate);
+        sb.append(", invoiceCurrency='").append(invoiceCurrency).append('\'');
+        sb.append(", invoiceBalance=").append(invoiceBalance);
+        sb.append(", invoiceAmountPaid=").append(invoiceAmountPaid);
+        sb.append(", invoiceAmountCharged=").append(invoiceAmountCharged);
+        sb.append(", invoiceOriginalAmountCharged=").append(invoiceOriginalAmountCharged);
+        sb.append(", invoiceAmountCredited=").append(invoiceAmountCredited);
+        sb.append(", invoicePaymentType='").append(invoicePaymentType).append('\'');
+        sb.append(", paymentNumber=").append(paymentNumber);
+        sb.append(", linkedInvoicePaymentId=").append(linkedInvoicePaymentId);
+        sb.append(", amount=").append(amount);
+        sb.append(", currency=").append(currency);
+        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 BusinessInvoicePaymentBaseModelDao that = (BusinessInvoicePaymentBaseModelDao) o;
+
+        if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
+            return false;
+        }
+        if (currency != that.currency) {
+            return false;
+        }
+        if (invoiceAmountCharged != null ? !invoiceAmountCharged.equals(that.invoiceAmountCharged) : that.invoiceAmountCharged != null) {
+            return false;
+        }
+        if (invoiceAmountCredited != null ? !invoiceAmountCredited.equals(that.invoiceAmountCredited) : that.invoiceAmountCredited != null) {
+            return false;
+        }
+        if (invoiceAmountPaid != null ? !invoiceAmountPaid.equals(that.invoiceAmountPaid) : that.invoiceAmountPaid != null) {
+            return false;
+        }
+        if (invoiceBalance != null ? !invoiceBalance.equals(that.invoiceBalance) : that.invoiceBalance != null) {
+            return false;
+        }
+        if (invoiceCreatedDate != null ? !invoiceCreatedDate.equals(that.invoiceCreatedDate) : that.invoiceCreatedDate != null) {
+            return false;
+        }
+        if (invoiceCurrency != null ? !invoiceCurrency.equals(that.invoiceCurrency) : that.invoiceCurrency != null) {
+            return false;
+        }
+        if (invoiceDate != null ? !invoiceDate.equals(that.invoiceDate) : that.invoiceDate != null) {
+            return false;
+        }
+        if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) {
+            return false;
+        }
+        if (invoiceNumber != null ? !invoiceNumber.equals(that.invoiceNumber) : that.invoiceNumber != null) {
+            return false;
+        }
+        if (invoiceOriginalAmountCharged != null ? !invoiceOriginalAmountCharged.equals(that.invoiceOriginalAmountCharged) : that.invoiceOriginalAmountCharged != null) {
+            return false;
+        }
+        if (invoicePaymentId != null ? !invoicePaymentId.equals(that.invoicePaymentId) : that.invoicePaymentId != null) {
+            return false;
+        }
+        if (invoicePaymentRecordId != null ? !invoicePaymentRecordId.equals(that.invoicePaymentRecordId) : that.invoicePaymentRecordId != null) {
+            return false;
+        }
+        if (invoicePaymentType != null ? !invoicePaymentType.equals(that.invoicePaymentType) : that.invoicePaymentType != null) {
+            return false;
+        }
+        if (invoiceTargetDate != null ? !invoiceTargetDate.equals(that.invoiceTargetDate) : that.invoiceTargetDate != null) {
+            return false;
+        }
+        if (linkedInvoicePaymentId != null ? !linkedInvoicePaymentId.equals(that.linkedInvoicePaymentId) : that.linkedInvoicePaymentId != null) {
+            return false;
+        }
+        if (paymentNumber != null ? !paymentNumber.equals(that.paymentNumber) : that.paymentNumber != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (invoicePaymentRecordId != null ? invoicePaymentRecordId.hashCode() : 0);
+        result = 31 * result + (invoicePaymentId != null ? invoicePaymentId.hashCode() : 0);
+        result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
+        result = 31 * result + (invoiceNumber != null ? invoiceNumber.hashCode() : 0);
+        result = 31 * result + (invoiceCreatedDate != null ? invoiceCreatedDate.hashCode() : 0);
+        result = 31 * result + (invoiceDate != null ? invoiceDate.hashCode() : 0);
+        result = 31 * result + (invoiceTargetDate != null ? invoiceTargetDate.hashCode() : 0);
+        result = 31 * result + (invoiceCurrency != null ? invoiceCurrency.hashCode() : 0);
+        result = 31 * result + (invoiceBalance != null ? invoiceBalance.hashCode() : 0);
+        result = 31 * result + (invoiceAmountPaid != null ? invoiceAmountPaid.hashCode() : 0);
+        result = 31 * result + (invoiceAmountCharged != null ? invoiceAmountCharged.hashCode() : 0);
+        result = 31 * result + (invoiceOriginalAmountCharged != null ? invoiceOriginalAmountCharged.hashCode() : 0);
+        result = 31 * result + (invoiceAmountCredited != null ? invoiceAmountCredited.hashCode() : 0);
+        result = 31 * result + (invoicePaymentType != null ? invoicePaymentType.hashCode() : 0);
+        result = 31 * result + (paymentNumber != null ? paymentNumber.hashCode() : 0);
+        result = 31 * result + (linkedInvoicePaymentId != null ? linkedInvoicePaymentId.hashCode() : 0);
+        result = 31 * result + (amount != null ? amount.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentChargebackModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentChargebackModelDao.java
new file mode 100644
index 0000000..b0805a1
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentChargebackModelDao.java
@@ -0,0 +1,46 @@
+/*
+ * 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.osgi.bundles.analytics.model;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.payment.api.Payment;
+import com.ning.billing.payment.api.PaymentMethod;
+import com.ning.billing.util.audit.AuditLog;
+
+public class BusinessInvoicePaymentChargebackModelDao extends BusinessInvoicePaymentBaseModelDao {
+
+    public BusinessInvoicePaymentChargebackModelDao(final Account account,
+                                                    final Invoice invoice,
+                                                    final InvoicePayment invoicePayment,
+                                                    final Payment payment,
+                                                    final PaymentMethod paymentMethod,
+                                                    final AuditLog creationAuditLog) {
+        super(account,
+              invoice,
+              invoicePayment,
+              payment,
+              paymentMethod,
+              creationAuditLog);
+    }
+
+    @Override
+    public String getTableName() {
+        return INVOICE_PAYMENT_CHARGEBACKS_TABLE_NAME;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentFieldModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentFieldModelDao.java
index d051bf0..78f5511 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentFieldModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentFieldModelDao.java
@@ -18,26 +18,56 @@ package com.ning.billing.osgi.bundles.analytics.model;
 
 import java.util.UUID;
 
+import org.joda.time.DateTime;
+
 public class BusinessInvoicePaymentFieldModelDao extends BusinessFieldModelDao {
 
-    private final UUID paymentId;
+    private static final String INVOICE_PAYMENT_FIELDS_TABLE_NAME = "bip_fields";
+
+    private final UUID invoicePaymentId;
+
+    public BusinessInvoicePaymentFieldModelDao(final Long customFieldRecordId,
+                                               final UUID invoicePaymentId,
+                                               final String name,
+                                               final String value,
+                                               final DateTime createdDate,
+                                               final String createdBy,
+                                               final String createdReasonCode,
+                                               final String createdComments,
+                                               final UUID accountId,
+                                               final String accountName,
+                                               final String accountExternalKey,
+                                               final Long accountRecordId,
+                                               final Long tenantRecordId) {
+        super(customFieldRecordId,
+              name,
+              value,
+              createdDate,
+              createdBy,
+              createdReasonCode,
+              createdComments,
+              accountId,
+              accountName,
+              accountExternalKey,
+              accountRecordId,
+              tenantRecordId);
+        this.invoicePaymentId = invoicePaymentId;
+    }
 
-    public BusinessInvoicePaymentFieldModelDao(final UUID paymentId, final String name, final String value) {
-        super(paymentId, name, value);
-        this.paymentId = paymentId;
+    @Override
+    public String getTableName() {
+        return INVOICE_PAYMENT_FIELDS_TABLE_NAME;
     }
 
-    public UUID getPaymentId() {
-        return paymentId;
+    public UUID getInvoicePaymentId() {
+        return invoicePaymentId;
     }
 
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
-        sb.append("BusinessPaymentField");
-        sb.append("{paymentId='").append(paymentId).append('\'');
-        sb.append(", name='").append(getName()).append('\'');
-        sb.append(", value='").append(getValue()).append('\'');
+        sb.append("BusinessInvoicePaymentFieldModelDao");
+        sb.append("{invoicePaymentId=").append(invoicePaymentId);
         sb.append('}');
         return sb.toString();
     }
@@ -50,16 +80,13 @@ public class BusinessInvoicePaymentFieldModelDao extends BusinessFieldModelDao {
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
+        if (!super.equals(o)) {
+            return false;
+        }
 
         final BusinessInvoicePaymentFieldModelDao that = (BusinessInvoicePaymentFieldModelDao) o;
 
-        if (paymentId != null ? !paymentId.equals(that.paymentId) : that.paymentId != null) {
-            return false;
-        }
-        if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
-            return false;
-        }
-        if (getValue() != null ? !getValue().equals(that.getValue()) : that.getValue() != null) {
+        if (invoicePaymentId != null ? !invoicePaymentId.equals(that.invoicePaymentId) : that.invoicePaymentId != null) {
             return false;
         }
 
@@ -68,9 +95,8 @@ public class BusinessInvoicePaymentFieldModelDao extends BusinessFieldModelDao {
 
     @Override
     public int hashCode() {
-        int result = paymentId != null ? paymentId.hashCode() : 0;
-        result = 31 * result + (getName() != null ? getName().hashCode() : 0);
-        result = 31 * result + (getValue() != null ? getValue().hashCode() : 0);
+        int result = super.hashCode();
+        result = 31 * result + (invoicePaymentId != null ? invoicePaymentId.hashCode() : 0);
         return result;
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentModelDao.java
index e04d1d6..fbacf20 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentModelDao.java
@@ -16,257 +16,31 @@
 
 package com.ning.billing.osgi.bundles.analytics.model;
 
-import java.math.BigDecimal;
-import java.util.UUID;
-
-import javax.annotation.Nullable;
-
-import org.joda.time.DateTime;
-
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.osgi.bundles.analytics.utils.Rounder;
-import com.ning.billing.util.entity.EntityBase;
-
-public class BusinessInvoicePaymentModelDao extends EntityBase {
-
-    private final UUID paymentId;
-    private final String extFirstPaymentRefId;
-    private final String extSecondPaymentRefId;
-    private final String accountKey;
-    private final UUID invoiceId;
-    private final DateTime effectiveDate;
-    private final BigDecimal amount;
-    private final Currency currency;
-    private final String paymentError;
-    private final String processingStatus;
-    private final BigDecimal requestedAmount;
-    private final String pluginName;
-    private final String paymentType;
-    private final String paymentMethod;
-    private final String cardType;
-    private final String cardCountry;
-    private final String invoicePaymentType;
-    private final UUID linkedInvoicePaymentId;
-
-    public BusinessInvoicePaymentModelDao(final String accountKey, final BigDecimal amount,
-                                          final String cardCountry, final String cardType, final DateTime createdDate,
-                                          final Currency currency, final DateTime effectiveDate, final UUID invoiceId,
-                                          final String paymentError, final UUID paymentId, final String paymentMethod,
-                                          final String paymentType, @Nullable final String pluginName, final String processingStatus,
-                                          final BigDecimal requestedAmount, final DateTime updatedDate, @Nullable final String invoicePaymentType,
-                                          @Nullable final UUID linkedInvoicePaymentId) {
-        super(paymentId, createdDate, updatedDate);
-        this.accountKey = accountKey;
-        this.amount = amount;
-        // TODO For backward compatibility
-        this.extFirstPaymentRefId = null;
-        this.extSecondPaymentRefId = null;
-        this.cardCountry = cardCountry;
-        this.cardType = cardType;
-        this.currency = currency;
-        this.effectiveDate = effectiveDate;
-        this.invoiceId = invoiceId;
-        this.paymentError = paymentError;
-        this.paymentId = paymentId;
-        this.paymentMethod = paymentMethod;
-        this.paymentType = paymentType;
-        this.pluginName = pluginName;
-        this.processingStatus = processingStatus;
-        this.requestedAmount = requestedAmount;
-        this.invoicePaymentType = invoicePaymentType;
-        this.linkedInvoicePaymentId = linkedInvoicePaymentId;
-    }
-
-    public String getExtFirstPaymentRefId() {
-        return extFirstPaymentRefId;
-    }
-
-    public String getExtSecondPaymentRefId() {
-        return extSecondPaymentRefId;
-    }
-
-    public UUID getPaymentId() {
-        return paymentId;
-    }
-
-    public String getAccountKey() {
-        return accountKey;
-    }
-
-    public BigDecimal getAmount() {
-        return amount;
-    }
-
-    public String getCardCountry() {
-        return cardCountry;
-    }
-
-    public String getCardType() {
-        return cardType;
-    }
-
-    public Currency getCurrency() {
-        return currency;
-    }
-
-    public DateTime getEffectiveDate() {
-        return effectiveDate;
-    }
-
-    public UUID getInvoiceId() {
-        return invoiceId;
-    }
-
-    public String getPaymentError() {
-        return paymentError;
-    }
-
-    public String getPaymentMethod() {
-        return paymentMethod;
-    }
-
-    public String getPaymentType() {
-        return paymentType;
-    }
-
-    public String getPluginName() {
-        return pluginName;
-    }
-
-    public String getProcessingStatus() {
-        return processingStatus;
-    }
-
-    public BigDecimal getRequestedAmount() {
-        return requestedAmount;
-    }
-
-    public String getInvoicePaymentType() {
-        return invoicePaymentType;
-    }
-
-    public UUID getLinkedInvoicePaymentId() {
-        return linkedInvoicePaymentId;
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("BusinessInvoicePaymentModelDao");
-        sb.append("{accountKey='").append(accountKey).append('\'');
-        sb.append(", paymentId=").append(paymentId);
-        sb.append(", createdDate=").append(createdDate);
-        sb.append(", extFirstPaymentRefId=").append(extFirstPaymentRefId);
-        sb.append(", updatedDate=").append(updatedDate);
-        sb.append(", invoiceId=").append(invoiceId);
-        sb.append(", effectiveDate=").append(effectiveDate);
-        sb.append(", amount=").append(amount);
-        sb.append(", currency=").append(currency);
-        sb.append(", paymentError='").append(paymentError).append('\'');
-        sb.append(", processingStatus='").append(processingStatus).append('\'');
-        sb.append(", requestedAmount=").append(requestedAmount);
-        sb.append(", pluginName='").append(pluginName).append('\'');
-        sb.append(", paymentType='").append(paymentType).append('\'');
-        sb.append(", paymentMethod='").append(paymentMethod).append('\'');
-        sb.append(", cardType='").append(cardType).append('\'');
-        sb.append(", cardCountry='").append(cardCountry).append('\'');
-        sb.append(", invoicePaymentType='").append(invoicePaymentType).append('\'');
-        sb.append(", linkedInvoicePaymentId='").append(linkedInvoicePaymentId).append('\'');
-        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 BusinessInvoicePaymentModelDao that = (BusinessInvoicePaymentModelDao) o;
-
-        if (accountKey != null ? !accountKey.equals(that.accountKey) : that.accountKey != null) {
-            return false;
-        }
-        if (amount != null ? Rounder.round(amount) != Rounder.round(that.amount) : that.amount != null) {
-            return false;
-        }
-        if (extFirstPaymentRefId != null ? !extFirstPaymentRefId.equals(that.extFirstPaymentRefId) : that.extFirstPaymentRefId != null) {
-            return false;
-        }
-        if (cardCountry != null ? !cardCountry.equals(that.cardCountry) : that.cardCountry != null) {
-            return false;
-        }
-        if (cardType != null ? !cardType.equals(that.cardType) : that.cardType != null) {
-            return false;
-        }
-        if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
-            return false;
-        }
-        if (currency != that.currency) {
-            return false;
-        }
-        if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
-            return false;
-        }
-        if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) {
-            return false;
-        }
-        if (paymentError != null ? !paymentError.equals(that.paymentError) : that.paymentError != null) {
-            return false;
-        }
-        if (paymentId != null ? !paymentId.equals(that.paymentId) : that.paymentId != null) {
-            return false;
-        }
-        if (paymentMethod != null ? !paymentMethod.equals(that.paymentMethod) : that.paymentMethod != null) {
-            return false;
-        }
-        if (paymentType != null ? !paymentType.equals(that.paymentType) : that.paymentType != null) {
-            return false;
-        }
-        if (pluginName != null ? !pluginName.equals(that.pluginName) : that.pluginName != null) {
-            return false;
-        }
-        if (processingStatus != null ? !processingStatus.equals(that.processingStatus) : that.processingStatus != null) {
-            return false;
-        }
-        if (requestedAmount != null ? Rounder.round(requestedAmount) != Rounder.round(that.requestedAmount) : that.requestedAmount != null) {
-            return false;
-        }
-        if (updatedDate != null ? !updatedDate.equals(that.updatedDate) : that.updatedDate != null) {
-            return false;
-        }
-        if (invoicePaymentType != null ? !invoicePaymentType.equals(that.invoicePaymentType) : that.invoicePaymentType != null) {
-            return false;
-        }
-        if (linkedInvoicePaymentId != null ? !linkedInvoicePaymentId.equals(that.linkedInvoicePaymentId) : that.linkedInvoicePaymentId != null) {
-            return false;
-        }
-
-        return true;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.payment.api.Payment;
+import com.ning.billing.payment.api.PaymentMethod;
+import com.ning.billing.util.audit.AuditLog;
+
+public class BusinessInvoicePaymentModelDao extends BusinessInvoicePaymentBaseModelDao {
+
+    public BusinessInvoicePaymentModelDao(final Account account,
+                                          final Invoice invoice,
+                                          final InvoicePayment invoicePayment,
+                                          final Payment payment,
+                                          final PaymentMethod paymentMethod,
+                                          final AuditLog creationAuditLog) {
+        super(account,
+              invoice,
+              invoicePayment,
+              payment,
+              paymentMethod,
+              creationAuditLog);
     }
 
     @Override
-    public int hashCode() {
-        int result = paymentId != null ? paymentId.hashCode() : 0;
-        result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
-        result = 31 * result + (extFirstPaymentRefId != null ? extFirstPaymentRefId.hashCode() : 0);
-        result = 31 * result + (updatedDate != null ? updatedDate.hashCode() : 0);
-        result = 31 * result + (accountKey != null ? accountKey.hashCode() : 0);
-        result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
-        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
-        result = 31 * result + (amount != null ? amount.hashCode() : 0);
-        result = 31 * result + (currency != null ? currency.hashCode() : 0);
-        result = 31 * result + (paymentError != null ? paymentError.hashCode() : 0);
-        result = 31 * result + (processingStatus != null ? processingStatus.hashCode() : 0);
-        result = 31 * result + (requestedAmount != null ? requestedAmount.hashCode() : 0);
-        result = 31 * result + (pluginName != null ? pluginName.hashCode() : 0);
-        result = 31 * result + (paymentType != null ? paymentType.hashCode() : 0);
-        result = 31 * result + (paymentMethod != null ? paymentMethod.hashCode() : 0);
-        result = 31 * result + (cardType != null ? cardType.hashCode() : 0);
-        result = 31 * result + (cardCountry != null ? cardCountry.hashCode() : 0);
-        return result;
+    public String getTableName() {
+        return INVOICE_PAYMENTS_TABLE_NAME;
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentRefundModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentRefundModelDao.java
new file mode 100644
index 0000000..40fbda1
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentRefundModelDao.java
@@ -0,0 +1,46 @@
+/*
+ * 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.osgi.bundles.analytics.model;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.payment.api.Payment;
+import com.ning.billing.payment.api.PaymentMethod;
+import com.ning.billing.util.audit.AuditLog;
+
+public class BusinessInvoicePaymentRefundModelDao extends BusinessInvoicePaymentBaseModelDao {
+
+    public BusinessInvoicePaymentRefundModelDao(final Account account,
+                                                final Invoice invoice,
+                                                final InvoicePayment invoicePayment,
+                                                final Payment payment,
+                                                final PaymentMethod paymentMethod,
+                                                final AuditLog creationAuditLog) {
+        super(account,
+              invoice,
+              invoicePayment,
+              payment,
+              paymentMethod,
+              creationAuditLog);
+    }
+
+    @Override
+    public String getTableName() {
+        return INVOICE_PAYMENT_REFUNDS_TABLE_NAME;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentTagModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentTagModelDao.java
index 92606cf..470ff55 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentTagModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoicePaymentTagModelDao.java
@@ -18,25 +18,37 @@ package com.ning.billing.osgi.bundles.analytics.model;
 
 import java.util.UUID;
 
+import org.joda.time.DateTime;
+
 public class BusinessInvoicePaymentTagModelDao extends BusinessTagModelDao {
 
-    private final UUID paymentId;
+    private static final String INVOICE_PAYMENT_TAGS_TABLE_NAME = "bip_tags";
+
+    private final UUID invoicePaymentId;
 
-    public BusinessInvoicePaymentTagModelDao(final UUID paymentId, final String name) {
-        super(name);
-        this.paymentId = paymentId;
+    public BusinessInvoicePaymentTagModelDao(final UUID invoicePaymentId, final String name, final DateTime createdDate, final String createdBy,
+                                             final String createdReasonCode, final String createdComments, final UUID accountId,
+                                             final String accountName, final String accountExternalKey, final Long accountRecordId,
+                                             final Long tenantRecordId) {
+        super(name, createdDate, createdBy, createdReasonCode, createdComments, accountId, accountName, accountExternalKey,
+              accountRecordId, tenantRecordId);
+        this.invoicePaymentId = invoicePaymentId;
+    }
+
+    @Override
+    public String getTableName() {
+        return INVOICE_PAYMENT_TAGS_TABLE_NAME;
     }
 
-    public UUID getPaymentId() {
-        return paymentId;
+    public UUID getInvoicePaymentId() {
+        return invoicePaymentId;
     }
 
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
-        sb.append("BusinessPaymentTag");
-        sb.append("{paymentId='").append(paymentId).append('\'');
-        sb.append(", name='").append(getName()).append('\'');
+        sb.append("BusinessInvoicePaymentTagModelDao");
+        sb.append("{invoicePaymentId=").append(invoicePaymentId);
         sb.append('}');
         return sb.toString();
     }
@@ -49,13 +61,13 @@ public class BusinessInvoicePaymentTagModelDao extends BusinessTagModelDao {
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
+        if (!super.equals(o)) {
+            return false;
+        }
 
         final BusinessInvoicePaymentTagModelDao that = (BusinessInvoicePaymentTagModelDao) o;
 
-        if (paymentId != null ? !paymentId.equals(that.paymentId) : that.paymentId != null) {
-            return false;
-        }
-        if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
+        if (invoicePaymentId != null ? !invoicePaymentId.equals(that.invoicePaymentId) : that.invoicePaymentId != null) {
             return false;
         }
 
@@ -64,8 +76,8 @@ public class BusinessInvoicePaymentTagModelDao extends BusinessTagModelDao {
 
     @Override
     public int hashCode() {
-        int result = paymentId != null ? paymentId.hashCode() : 0;
-        result = 31 * result + (getName() != null ? getName().hashCode() : 0);
+        int result = super.hashCode();
+        result = 31 * result + (invoicePaymentId != null ? invoicePaymentId.hashCode() : 0);
         return result;
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceTagModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceTagModelDao.java
index 2740d37..d0aa880 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceTagModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessInvoiceTagModelDao.java
@@ -18,15 +18,43 @@ package com.ning.billing.osgi.bundles.analytics.model;
 
 import java.util.UUID;
 
+import org.joda.time.DateTime;
+
 public class BusinessInvoiceTagModelDao extends BusinessTagModelDao {
 
+    private static final String INVOICE_TAGS_TABLE_NAME = "bin_tags";
+
     private final UUID invoiceId;
 
-    public BusinessInvoiceTagModelDao(final UUID invoiceId, final String name) {
-        super(name);
+    public BusinessInvoiceTagModelDao(final UUID invoiceId,
+                                      final String name,
+                                      final DateTime createdDate,
+                                      final String createdBy,
+                                      final String createdReasonCode,
+                                      final String createdComments,
+                                      final UUID accountId,
+                                      final String accountName,
+                                      final String accountExternalKey,
+                                      final Long accountRecordId,
+                                      final Long tenantRecordId) {
+        super(name,
+              createdDate,
+              createdBy,
+              createdReasonCode,
+              createdComments,
+              accountId,
+              accountName,
+              accountExternalKey,
+              accountRecordId,
+              tenantRecordId);
         this.invoiceId = invoiceId;
     }
 
+    @Override
+    public String getTableName() {
+        return INVOICE_TAGS_TABLE_NAME;
+    }
+
     public UUID getInvoiceId() {
         return invoiceId;
     }
@@ -35,8 +63,7 @@ public class BusinessInvoiceTagModelDao extends BusinessTagModelDao {
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         sb.append("BusinessInvoiceTagModelDao");
-        sb.append("{paymentId='").append(invoiceId).append('\'');
-        sb.append(", name='").append(getName()).append('\'');
+        sb.append("{invoiceId=").append(invoiceId);
         sb.append('}');
         return sb.toString();
     }
@@ -49,23 +76,23 @@ public class BusinessInvoiceTagModelDao extends BusinessTagModelDao {
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
+        if (!super.equals(o)) {
+            return false;
+        }
 
         final BusinessInvoiceTagModelDao that = (BusinessInvoiceTagModelDao) o;
 
         if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) {
             return false;
         }
-        if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
-            return false;
-        }
 
         return true;
     }
 
     @Override
     public int hashCode() {
-        int result = invoiceId != null ? invoiceId.hashCode() : 0;
-        result = 31 * result + (getName() != null ? getName().hashCode() : 0);
+        int result = super.hashCode();
+        result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
         return result;
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessModelDaoBase.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessModelDaoBase.java
new file mode 100644
index 0000000..411371c
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessModelDaoBase.java
@@ -0,0 +1,166 @@
+/*
+ * 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.osgi.bundles.analytics.model;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+public abstract class BusinessModelDaoBase {
+
+    protected final Long recordId;
+
+    protected final DateTime createdDate;
+    protected final String createdBy;
+    protected final String createdReasonCode;
+    protected final String createdComments;
+    protected final UUID accountId;
+    protected final String accountName;
+    protected final String accountExternalKey;
+    protected final Long accountRecordId;
+    protected final Long tenantRecordId;
+
+    public BusinessModelDaoBase(final DateTime createdDate, final String createdBy, final String createdReasonCode,
+                                final String createdComments, final UUID accountId, final String accountName,
+                                final String accountExternalKey, final Long accountRecordId, final Long tenantRecordId) {
+        recordId = null;
+        this.createdDate = createdDate;
+        this.createdBy = createdBy;
+        this.createdReasonCode = createdReasonCode;
+        this.createdComments = createdComments;
+        this.accountId = accountId;
+        this.accountName = accountName;
+        this.accountExternalKey = accountExternalKey;
+        this.accountRecordId = accountRecordId;
+        this.tenantRecordId = tenantRecordId;
+    }
+
+    public abstract String getTableName();
+
+    public Long getRecordId() {
+        return recordId;
+    }
+
+    public DateTime getCreatedDate() {
+        return createdDate;
+    }
+
+    public String getCreatedBy() {
+        return createdBy;
+    }
+
+    public String getCreatedReasonCode() {
+        return createdReasonCode;
+    }
+
+    public String getCreatedComments() {
+        return createdComments;
+    }
+
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    public String getAccountName() {
+        return accountName;
+    }
+
+    public String getAccountExternalKey() {
+        return accountExternalKey;
+    }
+
+    public Long getAccountRecordId() {
+        return accountRecordId;
+    }
+
+    public Long getTenantRecordId() {
+        return tenantRecordId;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessModelDaoBase");
+        sb.append("{createdDate=").append(createdDate);
+        sb.append(", createdBy='").append(createdBy).append('\'');
+        sb.append(", createdReasonCode='").append(createdReasonCode).append('\'');
+        sb.append(", createdComments='").append(createdComments).append('\'');
+        sb.append(", accountId=").append(accountId);
+        sb.append(", accountName='").append(accountName).append('\'');
+        sb.append(", accountExternalKey='").append(accountExternalKey).append('\'');
+        sb.append(", accountRecordId=").append(accountRecordId);
+        sb.append(", tenantRecordId=").append(tenantRecordId);
+        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 BusinessModelDaoBase that = (BusinessModelDaoBase) o;
+
+        if (accountExternalKey != null ? !accountExternalKey.equals(that.accountExternalKey) : that.accountExternalKey != null) {
+            return false;
+        }
+        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
+            return false;
+        }
+        if (accountName != null ? !accountName.equals(that.accountName) : that.accountName != null) {
+            return false;
+        }
+        if (accountRecordId != null ? !accountRecordId.equals(that.accountRecordId) : that.accountRecordId != null) {
+            return false;
+        }
+        if (createdBy != null ? !createdBy.equals(that.createdBy) : that.createdBy != null) {
+            return false;
+        }
+        if (createdComments != null ? !createdComments.equals(that.createdComments) : that.createdComments != null) {
+            return false;
+        }
+        if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
+            return false;
+        }
+        if (createdReasonCode != null ? !createdReasonCode.equals(that.createdReasonCode) : that.createdReasonCode != null) {
+            return false;
+        }
+        if (tenantRecordId != null ? !tenantRecordId.equals(that.tenantRecordId) : that.tenantRecordId != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = createdDate != null ? createdDate.hashCode() : 0;
+        result = 31 * result + (createdBy != null ? createdBy.hashCode() : 0);
+        result = 31 * result + (createdReasonCode != null ? createdReasonCode.hashCode() : 0);
+        result = 31 * result + (createdComments != null ? createdComments.hashCode() : 0);
+        result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
+        result = 31 * result + (accountName != null ? accountName.hashCode() : 0);
+        result = 31 * result + (accountExternalKey != null ? accountExternalKey.hashCode() : 0);
+        result = 31 * result + (accountRecordId != null ? accountRecordId.hashCode() : 0);
+        result = 31 * result + (tenantRecordId != null ? tenantRecordId.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessOverdueStatusModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessOverdueStatusModelDao.java
index 13540b1..b2e9f16 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessOverdueStatusModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessOverdueStatusModelDao.java
@@ -18,65 +18,90 @@ package com.ning.billing.osgi.bundles.analytics.model;
 
 import java.util.UUID;
 
-import javax.annotation.Nullable;
-
 import org.joda.time.DateTime;
 
-import com.ning.billing.util.entity.EntityBase;
+public class BusinessOverdueStatusModelDao extends BusinessModelDaoBase {
 
-public class BusinessOverdueStatusModelDao extends EntityBase {
+    private static final String OVERDUE_STATUS_TABLE_NAME = "bos";
 
-    private final String accountKey;
+    private final Long blockingStateRecordId;
     private final UUID bundleId;
-    private final String externalKey;
+    private final String bundleExternalKey;
     private final String status;
     private final DateTime startDate;
     private final DateTime endDate;
 
-    public BusinessOverdueStatusModelDao(final String accountKey, final UUID bundleId, @Nullable final DateTime endDate,
-                                         final String externalKey, final DateTime startDate, final String status) {
-        this.accountKey = accountKey;
+    public BusinessOverdueStatusModelDao(final Long blockingStateRecordId,
+                                         final UUID bundleId,
+                                         final String bundleExternalKey,
+                                         final String status,
+                                         final DateTime startDate,
+                                         final DateTime endDate,
+                                         final DateTime createdDate,
+                                         final String createdBy,
+                                         final String createdReasonCode,
+                                         final String createdComments,
+                                         final UUID accountId,
+                                         final String accountName,
+                                         final String accountExternalKey,
+                                         final Long accountRecordId,
+                                         final Long tenantRecordId) {
+        super(createdDate,
+              createdBy,
+              createdReasonCode,
+              createdComments,
+              accountId,
+              accountName,
+              accountExternalKey,
+              accountRecordId,
+              tenantRecordId);
+        this.blockingStateRecordId = blockingStateRecordId;
         this.bundleId = bundleId;
-        this.endDate = endDate;
-        this.externalKey = externalKey;
-        this.startDate = startDate;
+        this.bundleExternalKey = bundleExternalKey;
         this.status = status;
+        this.startDate = startDate;
+        this.endDate = endDate;
     }
 
-    public String getAccountKey() {
-        return accountKey;
+    @Override
+    public String getTableName() {
+        return OVERDUE_STATUS_TABLE_NAME;
+    }
+
+    public Long getBlockingStateRecordId() {
+        return blockingStateRecordId;
     }
 
     public UUID getBundleId() {
         return bundleId;
     }
 
-    public DateTime getEndDate() {
-        return endDate;
+    public String getBundleExternalKey() {
+        return bundleExternalKey;
     }
 
-    public String getExternalKey() {
-        return externalKey;
+    public String getStatus() {
+        return status;
     }
 
     public DateTime getStartDate() {
         return startDate;
     }
 
-    public String getStatus() {
-        return status;
+    public DateTime getEndDate() {
+        return endDate;
     }
 
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         sb.append("BusinessOverdueStatusModelDao");
-        sb.append("{accountKey=").append(accountKey);
-        sb.append(", bundleId='").append(bundleId).append('\'');
-        sb.append(", endDate='").append(endDate).append('\'');
-        sb.append(", externalKey='").append(externalKey).append('\'');
+        sb.append("{blockingStateRecordId=").append(blockingStateRecordId);
+        sb.append(", bundleId=").append(bundleId);
+        sb.append(", bundleExternalKey='").append(bundleExternalKey).append('\'');
         sb.append(", status='").append(status).append('\'');
         sb.append(", startDate=").append(startDate);
+        sb.append(", endDate=").append(endDate);
         sb.append('}');
         return sb.toString();
     }
@@ -89,19 +114,22 @@ public class BusinessOverdueStatusModelDao extends EntityBase {
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
+        if (!super.equals(o)) {
+            return false;
+        }
 
         final BusinessOverdueStatusModelDao that = (BusinessOverdueStatusModelDao) o;
 
-        if (accountKey != null ? !accountKey.equals(that.accountKey) : that.accountKey != null) {
+        if (blockingStateRecordId != null ? !blockingStateRecordId.equals(that.blockingStateRecordId) : that.blockingStateRecordId != null) {
             return false;
         }
-        if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
+        if (bundleExternalKey != null ? !bundleExternalKey.equals(that.bundleExternalKey) : that.bundleExternalKey != null) {
             return false;
         }
-        if (endDate != null ? !endDate.equals(that.endDate) : that.endDate != null) {
+        if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
             return false;
         }
-        if (externalKey != null ? !externalKey.equals(that.externalKey) : that.externalKey != null) {
+        if (endDate != null ? !endDate.equals(that.endDate) : that.endDate != null) {
             return false;
         }
         if (startDate != null ? !startDate.equals(that.startDate) : that.startDate != null) {
@@ -116,9 +144,10 @@ public class BusinessOverdueStatusModelDao extends EntityBase {
 
     @Override
     public int hashCode() {
-        int result = accountKey != null ? accountKey.hashCode() : 0;
+        int result = super.hashCode();
+        result = 31 * result + (blockingStateRecordId != null ? blockingStateRecordId.hashCode() : 0);
         result = 31 * result + (bundleId != null ? bundleId.hashCode() : 0);
-        result = 31 * result + (externalKey != null ? externalKey.hashCode() : 0);
+        result = 31 * result + (bundleExternalKey != null ? bundleExternalKey.hashCode() : 0);
         result = 31 * result + (status != null ? status.hashCode() : 0);
         result = 31 * result + (startDate != null ? startDate.hashCode() : 0);
         result = 31 * result + (endDate != null ? endDate.hashCode() : 0);
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 5bebb96..23d47a0 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
@@ -20,8 +20,6 @@ import java.math.BigDecimal;
 import java.math.RoundingMode;
 
 import org.joda.time.DateTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Catalog;
@@ -41,8 +39,6 @@ import static com.ning.billing.entitlement.api.user.Subscription.SubscriptionSta
  */
 public class BusinessSubscription {
 
-    private static final Logger log = LoggerFactory.getLogger(BusinessSubscription.class);
-
     private static final Currency USD = Currency.valueOf("USD");
 
     private final String productName;
@@ -55,12 +51,25 @@ public class BusinessSubscription {
     private final String priceList;
     private final BigDecimal mrr;
     private final String currency;
-    private final DateTime startDate;
     private final SubscriptionState state;
-
-    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 DateTime startDate, final SubscriptionState state) {
+    private final Boolean businessActive;
+    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;
@@ -71,8 +80,10 @@ public class BusinessSubscription {
         this.priceList = priceList;
         this.mrr = mrr;
         this.currency = currency;
-        this.startDate = startDate;
         this.state = state;
+        this.businessActive = businessActive;
+        this.startDate = startDate;
+        this.endDate = endDate;
     }
 
     /**
@@ -98,8 +109,7 @@ public class BusinessSubscription {
         try {
             thePlan = (currentPlan != null) ? catalog.findPlan(currentPlan, new DateTime(), startDate) : null;
             thePhase = (currentPhase != null) ? catalog.findPhase(currentPhase, new DateTime(), startDate) : null;
-        } catch (CatalogApiException e) {
-            log.error("Failed to retrieve Plan from catalog for plan {}, phase {}", currentPlan, currentPhase);
+        } catch (CatalogApiException ignored) {
         }
 
         this.priceList = priceList;
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 5d4d4c5..5e8b40b 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
@@ -16,9 +16,9 @@
 
 package com.ning.billing.osgi.bundles.analytics.model;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.ning.billing.catalog.api.Catalog;
 import com.ning.billing.catalog.api.CatalogApiException;
@@ -33,8 +33,6 @@ import static com.ning.billing.entitlement.api.user.Subscription.SubscriptionSta
  */
 public class BusinessSubscriptionEvent {
 
-    private static final Logger log = LoggerFactory.getLogger(BusinessSubscriptionEvent.class);
-
     private static final String MISC = "MISC";
 
     public enum EventType {
@@ -69,8 +67,7 @@ public class BusinessSubscriptionEvent {
         throw new IllegalArgumentException("Unable to parse event string: " + eventString);
     }
 
-    // Public for internal reasons
-    public BusinessSubscriptionEvent(final EventType eventType, final ProductCategory category) {
+    private BusinessSubscriptionEvent(final EventType eventType, final ProductCategory category) {
         this.eventType = eventType;
         this.category = category;
     }
@@ -119,16 +116,14 @@ public class BusinessSubscriptionEvent {
         Plan thePlan = null;
         try {
             thePlan = catalog.findPlan(plan, eventTime, subscriptionCreationDate);
-        } catch (CatalogApiException e) {
-            log.error(String.format("Failed to retrieve PLan from catalog for %s", plan));
-
+        } catch (CatalogApiException ignored) {
         }
+
         final ProductCategory category = getTypeFromSubscription(thePlan);
         return new BusinessSubscriptionEvent(eventType, category);
     }
 
-    private static ProductCategory getTypeFromSubscription(final Plan plan) {
-
+    private static ProductCategory getTypeFromSubscription(@Nullable final Plan plan) {
         if (plan != null && plan.getProduct() != null) {
             final Product product = plan.getProduct();
             if (product.getCatalogName() != null && product.getCategory() != null) {
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 8ca0eb8..cb9cc01 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
@@ -20,58 +20,56 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 
-import com.ning.billing.util.entity.EntityBase;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.util.events.SubscriptionInternalEvent;
 
 /**
  * Describe a state change between two BusinessSubscription
  */
-public class BusinessSubscriptionTransitionModelDao extends EntityBase {
+public class BusinessSubscriptionTransitionModelDao extends BusinessModelDaoBase {
 
-    private final long totalOrdering;
+    private final Long subscriptionEventRecordId;
+    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;
     private final BusinessSubscriptionEvent event;
     private final BusinessSubscription previousSubscription;
     private final BusinessSubscription nextSubscription;
 
-    public BusinessSubscriptionTransitionModelDao(final Long totalOrdering, final UUID bundleId, final String externalKey,
-                                                  final UUID accountId, final String accountKey, final UUID subscriptionId,
-                                                  final DateTime requestedTimestamp, final BusinessSubscriptionEvent event,
-                                                  final BusinessSubscription previousSubscription, final BusinessSubscription nextSubscription) {
-        if (totalOrdering == null) {
-            throw new IllegalArgumentException("A transition must have a total ordering");
-        }
-        if (bundleId == null) {
-            throw new IllegalArgumentException("A transition must have a bundle id");
-        }
-        if (externalKey == null) {
-            throw new IllegalArgumentException("A transition must have an external key");
-        }
-        if (accountId == null) {
-            throw new IllegalArgumentException("A transition must have an account key");
-        }
-        if (subscriptionId == null) {
-            throw new IllegalArgumentException("A transition must have a subscription id");
-        }
-        if (accountKey == null) {
-            throw new IllegalArgumentException("A transition must have an account key");
-        }
-        if (requestedTimestamp == null) {
-            throw new IllegalArgumentException("A transition must have a requested timestamp");
-        }
-        if (event == null) {
-            throw new IllegalArgumentException("No event specified");
-        }
-
+    public BusinessSubscriptionTransitionModelDao(final Long subscriptionEventRecordId,
+                                                  final Long totalOrdering,
+                                                  final UUID bundleId,
+                                                  final String bundleExternalKey,
+                                                  final UUID subscriptionId,
+                                                  final DateTime requestedTimestamp,
+                                                  final BusinessSubscriptionEvent event,
+                                                  final BusinessSubscription previousSubscription,
+                                                  final BusinessSubscription nextSubscription,
+                                                  final DateTime createdDate,
+                                                  final String createdBy,
+                                                  final String createdReasonCode,
+                                                  final String createdComments,
+                                                  final UUID accountId,
+                                                  final String accountName,
+                                                  final String accountExternalKey,
+                                                  final Long accountRecordId,
+                                                  final Long tenantRecordId) {
+        super(createdDate,
+              createdBy,
+              createdReasonCode,
+              createdComments,
+              accountId,
+              accountName,
+              accountExternalKey,
+              accountRecordId,
+              tenantRecordId);
+        this.subscriptionEventRecordId = subscriptionEventRecordId;
         this.totalOrdering = totalOrdering;
         this.bundleId = bundleId;
-        this.externalKey = externalKey;
-        this.accountId = accountId;
-        this.accountKey = accountKey;
+        this.bundleExternalKey = bundleExternalKey;
         this.subscriptionId = subscriptionId;
         this.requestedTimestamp = requestedTimestamp;
         this.event = event;
@@ -79,122 +77,34 @@ public class BusinessSubscriptionTransitionModelDao extends EntityBase {
         this.nextSubscription = nextSubscription;
     }
 
-    public long getTotalOrdering() {
-        return totalOrdering;
-    }
-
-    public BusinessSubscriptionEvent getEvent() {
-        return event;
-    }
-
-    public UUID getBundleId() {
-        return bundleId;
-    }
-
-    public String getExternalKey() {
-        return externalKey;
-    }
-
-    public UUID getAccountId() {
-        return accountId;
-    }
-
-    public String getAccountKey() {
-        return accountKey;
-    }
-
-    public BusinessSubscription getNextSubscription() {
-        return nextSubscription;
-    }
-
-    public BusinessSubscription getPreviousSubscription() {
-        return previousSubscription;
-    }
-
-    public UUID getSubscriptionId() {
-        return subscriptionId;
-    }
-
-    public DateTime getRequestedTimestamp() {
-        return requestedTimestamp;
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("BusinessSubscriptionTransitionModelDao");
-        sb.append("{accountId='").append(accountId).append('\'');
-        sb.append(", accountKey=").append(accountKey);
-        sb.append(", totalOrdering=").append(totalOrdering);
-        sb.append(", bundleId='").append(bundleId).append('\'');
-        sb.append(", externalKey='").append(externalKey).append('\'');
-        sb.append(", subscriptionId='").append(subscriptionId).append('\'');
-        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;
-        }
-
-        final BusinessSubscriptionTransitionModelDao that = (BusinessSubscriptionTransitionModelDao) o;
-
-        return totalOrdering == that.totalOrdering && isDuplicateOf(that);
-    }
-
-    @Override
-    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 + (event != null ? event.hashCode() : 0);
-        result = 31 * result + (previousSubscription != null ? previousSubscription.hashCode() : 0);
-        result = 31 * result + (nextSubscription != null ? nextSubscription.hashCode() : 0);
-        return result;
-    }
-
-    public boolean isDuplicateOf(final BusinessSubscriptionTransitionModelDao that) {
-        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 (event != null ? !event.equals(that.event) : that.event != null) {
-            return false;
-        }
-        if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
-            return false;
-        }
-        if (externalKey != null ? !externalKey.equals(that.externalKey) : that.externalKey != 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 (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
-            return false;
-        }
-        if (requestedTimestamp != null ? !requestedTimestamp.equals(that.requestedTimestamp) : that.requestedTimestamp != null) {
-            return false;
-        }
-
-        return true;
+    public BusinessSubscriptionTransitionModelDao(final Account account,
+                                                  final SubscriptionBundle bundle,
+                                                  final SubscriptionInternalEvent subscriptionEvent,
+                                                  final DateTime requestedTimestamp,
+                                                  final BusinessSubscriptionEvent event,
+                                                  final BusinessSubscription previousSubscription,
+                                                  final BusinessSubscription nextSubscription,
+                                                  final String createdBy,
+                                                  final String createdReasonCode,
+                                                  final String createdComments) {
+        this(null /* TODO */,
+             subscriptionEvent.getTotalOrdering(),
+             bundle.getId(),
+             bundle.getExternalKey(),
+             subscriptionEvent.getSubscriptionId(),
+             requestedTimestamp,
+             event,
+             previousSubscription,
+             nextSubscription,
+             null /* TODO */,
+             createdBy,
+             createdReasonCode,
+             createdComments,
+             account.getId(),
+             account.getName(),
+             account.getExternalKey(),
+             // TODO
+             null,
+             null);
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessTagModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessTagModelDao.java
index 9809b57..5da3b7f 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessTagModelDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/model/BusinessTagModelDao.java
@@ -16,17 +16,74 @@
 
 package com.ning.billing.osgi.bundles.analytics.model;
 
-import com.ning.billing.util.entity.EntityBase;
+import java.util.UUID;
 
-public abstract class BusinessTagModelDao extends EntityBase {
+import org.joda.time.DateTime;
+
+public abstract class BusinessTagModelDao extends BusinessModelDaoBase {
 
     private final String name;
 
-    public BusinessTagModelDao(final String name) {
+    protected BusinessTagModelDao(final String name,
+                                  final DateTime createdDate,
+                                  final String createdBy,
+                                  final String createdReasonCode,
+                                  final String createdComments,
+                                  final UUID accountId,
+                                  final String accountName,
+                                  final String accountExternalKey,
+                                  final Long accountRecordId,
+                                  final Long tenantRecordId) {
+        super(createdDate,
+              createdBy,
+              createdReasonCode,
+              createdComments,
+              accountId,
+              accountName,
+              accountExternalKey,
+              accountRecordId,
+              tenantRecordId);
         this.name = name;
     }
 
     public String getName() {
         return name;
     }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessTagModelDao");
+        sb.append("{name='").append(name).append('\'');
+        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 BusinessTagModelDao that = (BusinessTagModelDao) o;
+
+        if (name != null ? !name.equals(that.name) : that.name != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (name != null ? name.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 7c93a84..9b16fd3 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
@@ -1,15 +1,15 @@
 /*! SET storage_engine=INNODB */;
 
+-- Subscription events
 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
-, account_id char(36) not null
-, external_key varchar(50) not null comment 'Bundle external key'
-, account_key varchar(50) not null comment 'Account external key'
+, bundle_external_key varchar(50) not null
 , subscription_id char(36) not null
-, requested_timestamp bigint not null
+, requested_timestamp datetime not null
 , event varchar(50) not null
 , prev_product_name varchar(50) default null
 , prev_product_type varchar(50) default null
@@ -21,8 +21,9 @@ create table bst (
 , prev_price_list varchar(50) default null
 , prev_mrr numeric(10, 4) default 0
 , prev_currency varchar(50) default null
-, prev_start_date bigint default null
 , prev_state varchar(50) default null
+, prev_business_active bool default true
+, prev_start_date datetime default null
 , next_product_name varchar(50) default null
 , next_product_type varchar(50) default null
 , next_product_category varchar(50) default null
@@ -33,70 +34,160 @@ create table bst (
 , next_price_list varchar(50) default null
 , next_mrr numeric(10, 4) default 0
 , next_currency varchar(50) default null
-, next_start_date bigint default null
 , next_state varchar(50) default null
+, next_business_active bool default true
+, next_start_date datetime default null
+, next_end_date datetime default null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
 , account_record_id int(11) unsigned default null
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create index bst_key_index on bst (external_key, requested_timestamp asc);
+create index bst_bundle_external_key on bst(bundle_external_key);
+create index bst_account_id on bst(account_id);
 create index bst_tenant_account_record_id on bst(tenant_record_id, account_record_id);
 
+-- Accounts
 drop table if exists bac;
 create table bac (
   record_id int(11) unsigned not null auto_increment
-, account_id char(36) not null
-, account_key varchar(50) not null
-, name varchar(100) not null
-, created_date bigint not null
-, updated_date bigint not null
+, email varchar(128) not null
+, first_name_length int not null
+, currency char(3) default null
+, billing_cycle_day_local int default null
+, payment_method_id char(36) default null
+, time_zone varchar(50) default null
+, locale varchar(5) default null
+, address1 varchar(100) default null
+, address2 varchar(100) default null
+, company_name varchar(50) default null
+, city varchar(50) default null
+, state_or_province varchar(50) default null
+, country varchar(50) default null
+, postal_code varchar(16) default null
+, phone varchar(25) default null
+, migrated bool default false
+, is_notified_for_invoices boolean not null
 , balance numeric(10, 4) default 0
 , last_invoice_date date default null
-, total_invoice_balance numeric(10, 4) default 0
+, last_payment_date datetime not null
 , last_payment_status varchar(255) default null
-, payment_method varchar(50) default null
-, credit_card_type varchar(50) default null
-, billing_address_country varchar(50) default null
-, currency char(50) default null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, updated_date datetime not null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
 , account_record_id int(11) unsigned default null
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create unique index bac_key_index on bac (account_key);
+create unique index bac_account_external_key on bac(account_external_key);
+create unique index bac_account_id on bac(account_id);
 create index bac_tenant_account_record_id on bac(tenant_record_id, account_record_id);
 
+-- Invoices
 drop table if exists bin;
 create table bin (
   record_id int(11) unsigned not null auto_increment
+, invoice_record_id int(11) unsigned default null
 , invoice_id char(36) not null
 , invoice_number bigint default null
-, created_date bigint not null
-, updated_date bigint not null
-, account_id char(36) not null
-, account_key varchar(50) not null
 , invoice_date date not null
 , target_date date not null
 , currency char(50) not null
-, balance numeric(10, 4) default 0 comment 'amount_charged - amount_paid - amount_credited'
-, amount_paid numeric(10, 4) default 0 comment 'Sums of the successful payments made for this invoice minus the refunds associated with this invoice'
-, amount_charged numeric(10, 4) default 0 comment 'Sums of the invoice items amount'
-, amount_credited numeric(10, 4) default 0 comment 'Sums of the credit items'
+, balance numeric(10, 4) default 0
+, amount_paid numeric(10, 4) default 0
+, amount_charged numeric(10, 4) default 0
+, original_amount_charged numeric(10, 4) default 0
+, amount_credited numeric(10, 4) default 0
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
 , account_record_id int(11) unsigned default null
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create unique index bin_key_index on bin (invoice_id);
+create unique index bin_invoice_record_id on bin(invoice_record_id);
+create unique index bin_invoice_id on bin(invoice_id);
 create index bin_tenant_account_record_id on bin(tenant_record_id, account_record_id);
 
+-- Invoice adjustments (type REFUND_ADJ)
+drop table if exists bia;
+create table bia (
+  record_id int(11) unsigned not null auto_increment
+, invoice_item_record_id int(11) unsigned default null
+, item_id char(36) not null
+, invoice_id char(36) not null
+, invoice_number bigint default null
+, invoice_created_date date not null
+, invoice_date date not null
+, invoice_target_date date not null
+, invoice_currency char(50) not null
+, invoice_balance numeric(10, 4) default 0
+, invoice_amount_paid numeric(10, 4) default 0
+, invoice_amount_charged numeric(10, 4) default 0
+, invoice_original_amount_charged numeric(10, 4) default 0
+, invoice_amount_credited numeric(10, 4) default 0
+, item_type char(50) not null
+, bundle_external_key varchar(50) default null
+, product_name varchar(50) default null
+, product_type varchar(50) default null
+, product_category varchar(50) default null
+, slug varchar(50) default null
+, phase varchar(50) default null
+, billing_period varchar(50) default null
+, start_date date default null
+, end_date date default null
+, amount numeric(10, 4) default 0
+, currency char(50) default null
+, linked_item_id char(36) default null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
+, account_record_id int(11) unsigned default null
+, tenant_record_id int(11) unsigned default null
+, primary key(record_id)
+);
+create unique index bia_invoice_item_record_id on bia(invoice_item_record_id);
+create unique index bia_item_id on bia(item_id);
+create index bia_tenant_account_record_id on bia(tenant_record_id, account_record_id);
+
+-- Invoice items (without adjustments, type EXTERNAL_CHARGE, FIXED and RECURRING)
 drop table if exists bii;
 create table bii (
   record_id int(11) unsigned not null auto_increment
+, invoice_item_record_id int(11) unsigned default null
 , item_id char(36) not null
-, created_date bigint not null
-, updated_date bigint not null
 , invoice_id char(36) not null
-, item_type char(50) not null comment 'e.g. FIXED or RECURRING'
-, external_key varchar(50) default null comment 'Bundle external key (could be null for certain items)'
+, invoice_number bigint default null
+, invoice_created_date date not null
+, invoice_date date not null
+, invoice_target_date date not null
+, invoice_currency char(50) not null
+, invoice_balance numeric(10, 4) default 0
+, invoice_amount_paid numeric(10, 4) default 0
+, invoice_amount_charged numeric(10, 4) default 0
+, invoice_original_amount_charged numeric(10, 4) default 0
+, invoice_amount_credited numeric(10, 4) default 0
+, item_type char(50) not null
+, bundle_external_key varchar(50) default null
 , product_name varchar(50) default null
 , product_type varchar(50) default null
 , product_category varchar(50) default null
@@ -108,150 +199,356 @@ create table bii (
 , amount numeric(10, 4) default 0
 , currency char(50) default null
 , linked_item_id char(36) default null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
 , account_record_id int(11) unsigned default null
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create unique index bii_key_index on bii (item_id);
+create unique index bii_invoice_item_record_id on bii(invoice_item_record_id);
+create unique index bii_item_id on bii(item_id);
 create index bii_tenant_account_record_id on bii(tenant_record_id, account_record_id);
 
-drop table if exists bip;
-create table bip (
+-- Invoice items adjustments (type ITEM_ADJ)
+drop table if exists biia;
+create table biia (
   record_id int(11) unsigned not null auto_increment
-, payment_id char(36) not null
-, created_date bigint not null
-, updated_date bigint not null
-, ext_first_payment_ref_id varchar(255) default null
-, ext_second_payment_ref_id varchar(255) default null
-, account_key varchar(50) not null comment 'Account external key'
+, invoice_item_record_id int(11) unsigned default null
+, item_id char(36) not null
 , invoice_id char(36) not null
-, effective_date bigint default null
+, invoice_number bigint default null
+, invoice_created_date date not null
+, invoice_date date not null
+, invoice_target_date date not null
+, invoice_currency char(50) not null
+, invoice_balance numeric(10, 4) default 0
+, invoice_amount_paid numeric(10, 4) default 0
+, invoice_amount_charged numeric(10, 4) default 0
+, invoice_original_amount_charged numeric(10, 4) default 0
+, invoice_amount_credited numeric(10, 4) default 0
+, item_type char(50) not null
+, bundle_external_key varchar(50) default null
+, product_name varchar(50) default null
+, product_type varchar(50) default null
+, product_category varchar(50) default null
+, slug varchar(50) default null
+, phase varchar(50) default null
+, billing_period varchar(50) default null
+, start_date date default null
+, end_date date default null
 , amount numeric(10, 4) default 0
 , currency char(50) default null
-, payment_error varchar(255) default null
-, processing_status varchar(50) default null
-, requested_amount numeric(10, 4) default 0
-, plugin_name varchar(50) default null
-, payment_type varchar(50) default null
-, payment_method varchar(50) default null
-, card_type varchar(50) default null
-, card_country varchar(50) default null
-, invoice_payment_type varchar(50) default null
-, linked_invoice_payment_id char(36) default null
+, linked_item_id char(36) default null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
 , account_record_id int(11) unsigned default null
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create unique index bip_key_index on bip (payment_id);
-create index bip_tenant_account_record_id on bip(tenant_record_id, account_record_id);
+create unique index biia_invoice_item_record_id on biia(invoice_item_record_id);
+create unique index biia_item_id on biia(item_id);
+create index biia_tenant_account_record_id on biia(tenant_record_id, account_record_id);
 
-drop table if exists bos;
-create table bos (
+-- Account credits (type CBA_ADJ (< 0 only, for applied credits) and CREDIT_ADJ)
+drop table if exists biic;
+create table biic (
   record_id int(11) unsigned not null auto_increment
-, bundle_id char(36) not null
-, external_key varchar(50) not null comment 'Bundle external key'
-, account_key varchar(50) not null comment 'Account external key'
-, status varchar(50) not null
-, start_date bigint default null
-, end_date bigint default null
+, invoice_item_record_id int(11) unsigned default null
+, item_id char(36) not null
+, invoice_id char(36) not null
+, invoice_number bigint default null
+, invoice_created_date date not null
+, invoice_date date not null
+, invoice_target_date date not null
+, invoice_currency char(50) not null
+, invoice_balance numeric(10, 4) default 0
+, invoice_amount_paid numeric(10, 4) default 0
+, invoice_amount_charged numeric(10, 4) default 0
+, invoice_original_amount_charged numeric(10, 4) default 0
+, invoice_amount_credited numeric(10, 4) default 0
+, item_type char(50) not null
+, recognizable bool default true
+, bundle_external_key varchar(50) default null
+, product_name varchar(50) default null
+, product_type varchar(50) default null
+, product_category varchar(50) default null
+, slug varchar(50) default null
+, phase varchar(50) default null
+, billing_period varchar(50) default null
+, start_date date default null
+, end_date date default null
+, amount numeric(10, 4) default 0
+, currency char(50) default null
+, linked_item_id char(36) default null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
 , account_record_id int(11) unsigned default null
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create index bos_tenant_account_record_id on bos(tenant_record_id, account_record_id);
+create unique index biic_invoice_item_record_id on biic(invoice_item_record_id);
+create unique index biic_item_id on biic(item_id);
+create index biic_tenant_account_record_id on biic(tenant_record_id, account_record_id);
 
-drop table if exists bac_tags;
-create table bac_tags (
+-- Invoice payments
+drop table if exists bip;
+create table bip (
   record_id int(11) unsigned not null auto_increment
+, invoice_payment_record_id int(11) unsigned default null
+, invoice_payment_id char(36) not null
+, invoice_id char(36) not null
+, invoice_number bigint default null
+, invoice_created_date date not null
+, invoice_date date not null
+, invoice_target_date date not null
+, invoice_currency char(50) not null
+, invoice_balance numeric(10, 4) default 0
+, invoice_amount_paid numeric(10, 4) default 0
+, invoice_amount_charged numeric(10, 4) default 0
+, invoice_original_amount_charged numeric(10, 4) default 0
+, invoice_amount_credited numeric(10, 4) default 0
+, invoice_payment_type varchar(50) default null
+, payment_number bigint default null
+, linked_invoice_payment_id char(36) default null
+, amount numeric(10, 4) default 0
+, currency char(50) default null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
 , account_id char(36) not null
-, account_key varchar(50) not null comment 'Account external key'
-, name varchar(50) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
 , account_record_id int(11) unsigned default null
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create index bac_tags_tenant_account_record_id on bac_tags(tenant_record_id, account_record_id);
+create unique index bip_invoice_payment_record_id on bip(invoice_payment_record_id);
+create unique index bip_invoice_payment_id on bip(invoice_payment_id);
+create index bip_tenant_account_record_id on bip(tenant_record_id, account_record_id);
 
-drop table if exists bac_fields;
-create table bac_fields (
+-- Invoice refunds
+drop table if exists bipr;
+create table bipr (
   record_id int(11) unsigned not null auto_increment
+, invoice_payment_record_id int(11) unsigned default null
+, invoice_payment_id char(36) not null
+, invoice_id char(36) not null
+, invoice_number bigint default null
+, invoice_created_date date not null
+, invoice_date date not null
+, invoice_target_date date not null
+, invoice_currency char(50) not null
+, invoice_balance numeric(10, 4) default 0
+, invoice_amount_paid numeric(10, 4) default 0
+, invoice_amount_charged numeric(10, 4) default 0
+, invoice_original_amount_charged numeric(10, 4) default 0
+, invoice_amount_credited numeric(10, 4) default 0
+, payment_id char(36) not null
+, payment_number bigint default null
+, payment_amount numeric(10, 4) default 0
+, payment_currency char(50) default null
+, amount numeric(10, 4) default 0
+, currency char(50) default null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
 , account_id char(36) not null
-, account_key varchar(50) not null comment 'Account external key'
-, name varchar(50) not null
-, value varchar(255) default null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
 , account_record_id int(11) unsigned default null
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create index bac_fields_tenant_account_record_id on bac_fields(tenant_record_id, account_record_id);
+create unique index bipr_invoice_payment_record_id on bipr(invoice_payment_record_id);
+create unique index bipr_invoice_payment_id on bipr(invoice_payment_id);
+create index bipr_tenant_account_record_id on bipr(tenant_record_id, account_record_id);
 
-drop table if exists bst_tags;
-create table bst_tags (
+-- Invoice payment chargebacks
+drop table if exists bipc;
+create table bipc (
   record_id int(11) unsigned not null auto_increment
-, bundle_id char(36) not null
-, external_key varchar(50) not null comment 'Bundle external key'
-, account_key varchar(50) not null comment 'Account external key'
-, name varchar(50) not null
+, invoice_payment_record_id int(11) unsigned default null
+, invoice_payment_id char(36) not null
+, invoice_id char(36) not null
+, invoice_number bigint default null
+, invoice_created_date date not null
+, invoice_date date not null
+, invoice_target_date date not null
+, invoice_currency char(50) not null
+, invoice_balance numeric(10, 4) default 0
+, invoice_amount_paid numeric(10, 4) default 0
+, invoice_amount_charged numeric(10, 4) default 0
+, invoice_original_amount_charged numeric(10, 4) default 0
+, invoice_amount_credited numeric(10, 4) default 0
+, payment_id char(36) not null
+, payment_number bigint default null
+, payment_amount numeric(10, 4) default 0
+, payment_currency char(50) default null
+, amount numeric(10, 4) default 0
+, currency char(50) default null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
 , account_record_id int(11) unsigned default null
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create index bst_tags_tenant_account_record_id on bst_tags(tenant_record_id, account_record_id);
+create unique index bipc_invoice_payment_record_id on bipc(invoice_payment_record_id);
+create unique index bipc_invoice_payment_id on bipc(invoice_payment_id);
+create index bipc_tenant_account_record_id on bipc(tenant_record_id, account_record_id);
 
-drop table if exists bst_fields;
-create table bst_fields (
+drop table if exists bos;
+create table bos (
   record_id int(11) unsigned not null auto_increment
+, blocking_state_record_id int(11) unsigned default null
 , bundle_id char(36) not null
-, external_key varchar(50) not null comment 'Bundle external key'
-, account_key varchar(50) not null comment 'Account external key'
+, bundle_external_key varchar(50) not null
+, status varchar(50) not null
+, start_date datetime default null
+, end_date datetime default null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
+, account_record_id int(11) unsigned default null
+, tenant_record_id int(11) unsigned default null
+, primary key(record_id)
+);
+create index bos_tenant_account_record_id on bos(tenant_record_id, account_record_id);
+
+drop table if exists bac_tags;
+create table bac_tags (
+  record_id int(11) unsigned not null auto_increment
+, tag_record_id int(11) unsigned default null
 , name varchar(50) not null
-, value varchar(255) default null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
 , account_record_id int(11) unsigned default null
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create index bst_fields_tenant_account_record_id on bst_fields(tenant_record_id, account_record_id);
+create index bac_tags_tenant_account_record_id on bac_tags(tenant_record_id, account_record_id);
 
 drop table if exists bin_tags;
 create table bin_tags (
   record_id int(11) unsigned not null auto_increment
+, tag_record_id int(11) unsigned default null
 , invoice_id char(36) not null
 , name varchar(50) not null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
 , account_record_id int(11) unsigned default null
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
 create index bin_tags_tenant_account_record_id on bin_tags(tenant_record_id, account_record_id);
 
-drop table if exists bin_fields;
-create table bin_fields (
+drop table if exists bip_tags;
+create table bip_tags (
   record_id int(11) unsigned not null auto_increment
-, invoice_id char(36) not null
+, tag_record_id int(11) unsigned default null
+, invoice_payment_id char(36) not null
+, name varchar(50) not null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
+, account_record_id int(11) unsigned default null
+, tenant_record_id int(11) unsigned default null
+, primary key(record_id)
+);
+create index bip_tags_tenant_account_record_id on bip_tags(tenant_record_id, account_record_id);
+
+drop table if exists bac_fields;
+create table bac_fields (
+  record_id int(11) unsigned not null auto_increment
+, custom_field_record_id int(11) unsigned default null
 , name varchar(50) not null
 , value varchar(255) default null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
 , account_record_id int(11) unsigned default null
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create index bin_fields_tenant_account_record_id on bin_fields(tenant_record_id, account_record_id);
+create index bac_fields_tenant_account_record_id on bac_fields(tenant_record_id, account_record_id);
 
-drop table if exists bip_tags;
-create table bip_tags (
+drop table if exists bin_fields;
+create table bin_fields (
   record_id int(11) unsigned not null auto_increment
-, payment_id char(36) not null
+, custom_field_record_id int(11) unsigned default null
+, invoice_id char(36) not null
 , name varchar(50) not null
+, value varchar(255) default null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
 , account_record_id int(11) unsigned default null
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create index bip_tags_tenant_account_record_id on bip_tags(tenant_record_id, account_record_id);
+create index bin_fields_tenant_account_record_id on bin_fields(tenant_record_id, account_record_id);
 
 drop table if exists bip_fields;
 create table bip_fields (
   record_id int(11) unsigned not null auto_increment
-, payment_id char(36) not null
+, custom_field_record_id int(11) unsigned default null
+, invoice_payment_id char(36) not null
 , name varchar(50) not null
 , value varchar(255) default null
+, created_date datetime not null
+, created_by varchar(50) not null
+, created_reason_code varchar(255) default null
+, created_comments varchar(255) default null
+, account_id char(36) not null
+, account_name varchar(100) not null
+, account_external_key varchar(50) not null
 , account_record_id int(11) unsigned default null
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/AnalyticsTestSuiteNoDB.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/AnalyticsTestSuiteNoDB.java
index e096376..dd2bba8 100644
--- a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/AnalyticsTestSuiteNoDB.java
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/AnalyticsTestSuiteNoDB.java
@@ -27,7 +27,6 @@ import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.invoice.dao.InvoiceDao;
-import com.ning.billing.osgi.bundles.analytics.api.DefaultAnalyticsService;
 import com.ning.billing.osgi.bundles.analytics.dao.BusinessAccountFieldSqlDao;
 import com.ning.billing.osgi.bundles.analytics.dao.BusinessAccountSqlDao;
 import com.ning.billing.osgi.bundles.analytics.dao.BusinessAccountTagSqlDao;
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/AnalyticsTestSuiteWithEmbeddedDB.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/AnalyticsTestSuiteWithEmbeddedDB.java
index ae239d4..974e701 100644
--- a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/AnalyticsTestSuiteWithEmbeddedDB.java
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/AnalyticsTestSuiteWithEmbeddedDB.java
@@ -28,7 +28,6 @@ import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.invoice.dao.InvoiceDao;
-import com.ning.billing.osgi.bundles.analytics.api.DefaultAnalyticsService;
 import com.ning.billing.osgi.bundles.analytics.dao.BusinessAccountFieldSqlDao;
 import com.ning.billing.osgi.bundles.analytics.dao.BusinessAccountSqlDao;
 import com.ning.billing.osgi.bundles.analytics.dao.BusinessAccountTagSqlDao;
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessSubscriptionTransitionFieldSqlDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessSubscriptionTransitionFieldSqlDao.java
index c6e0618..3a165aa 100644
--- a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessSubscriptionTransitionFieldSqlDao.java
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessSubscriptionTransitionFieldSqlDao.java
@@ -23,7 +23,6 @@ import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteWithEmbeddedDB;
-import com.ning.billing.osgi.bundles.analytics.model.BusinessSubscriptionTransitionFieldModelDao;
 
 public class TestBusinessSubscriptionTransitionFieldSqlDao extends AnalyticsTestSuiteWithEmbeddedDB {
 
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessSubscriptionTransitionTagSqlDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessSubscriptionTransitionTagSqlDao.java
index 9681b10..c69a775 100644
--- a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessSubscriptionTransitionTagSqlDao.java
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessSubscriptionTransitionTagSqlDao.java
@@ -23,7 +23,6 @@ import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteWithEmbeddedDB;
-import com.ning.billing.osgi.bundles.analytics.model.BusinessSubscriptionTransitionTagModelDao;
 
 public class TestBusinessSubscriptionTransitionTagSqlDao extends AnalyticsTestSuiteWithEmbeddedDB {
 
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/glue/TestAnalyticsModule.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/glue/TestAnalyticsModule.java
index e15543a..f32f940 100644
--- a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/glue/TestAnalyticsModule.java
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/glue/TestAnalyticsModule.java
@@ -28,7 +28,6 @@ import com.ning.billing.mock.glue.MockInvoiceModule;
 import com.ning.billing.mock.glue.MockJunctionModule;
 import com.ning.billing.mock.glue.MockOverdueModule;
 import com.ning.billing.mock.glue.MockPaymentModule;
-import com.ning.billing.osgi.bundles.analytics.setup.AnalyticsModule;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.util.glue.AuditModule;
 import com.ning.billing.util.glue.CacheModule;
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/TestBusinessSubscriptionTransitionRecorder.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/TestBusinessSubscriptionTransitionRecorder.java
index 5330aeb..4beae31 100644
--- a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/TestBusinessSubscriptionTransitionRecorder.java
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/TestBusinessSubscriptionTransitionRecorder.java
@@ -75,7 +75,7 @@ public class TestBusinessSubscriptionTransitionRecorder extends AnalyticsTestSui
 
         Mockito.when(entitlementInternalApi.getSubscriptionsForBundle(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(ImmutableList.<Subscription>of(subscription));
 
-        subscriptionTransitionDao.rebuildTransitionsForBundle(bundle.getId(), internalCallContext);
+        subscriptionTransitionDao.update(bundle.getId(), internalCallContext);
 
         Assert.assertEquals(subscriptionTransitionSqlDao.getTransitionsByKey(externalKey.toString(), internalCallContext).size(), 1);
         final BusinessSubscriptionTransitionModelDao transition = subscriptionTransitionSqlDao.getTransitionsByKey(externalKey.toString(), internalCallContext).get(0);

pom.xml 16(+16 -0)

diff --git a/pom.xml b/pom.xml
index 6a24b00..fd07e5c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,6 +42,7 @@
     </scm>
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <killbill-commons.version>0.1.0-SNAPSHOT</killbill-commons.version>
         <slf4j.version>1.7.2</slf4j.version>
         <ehcache.version>2.6.2</ehcache.version>
     </properties>
@@ -309,6 +310,21 @@
                 <version>${project.version}</version>
             </dependency>
             <dependency>
+                <groupId>com.ning.billing.commons</groupId>
+                <artifactId>killbill-embeddeddb</artifactId>
+                <version>${killbill-commons.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing.commons</groupId>
+                <artifactId>killbill-jdbi</artifactId>
+                <version>${killbill-commons.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing.commons</groupId>
+                <artifactId>killbill-locker</artifactId>
+                <version>${killbill-commons.version}</version>
+            </dependency>
+            <dependency>
                 <groupId>com.jolbox</groupId>
                 <artifactId>bonecp</artifactId>
                 <version>0.7.1.RELEASE</version>