killbill-memoizeit

Merge branch 'analytics-as-osgi-plugin' of github.com:killbill/killbill

4/11/2013 5:11:18 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/DefaultBusinessSubscriptionTransition.java 497(+0 -497)

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/api/user/DefaultAnalyticsUserApi.java 395(+0 -395)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessAccountDao.java 154(+0 -154)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessInvoiceDao.java 221(+0 -221)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessInvoicePaymentDao.java 203(+0 -203)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessOverdueStatusDao.java 120(+0 -120)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessSubscriptionTransitionDao.java 261(+0 -261)

osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessTagDao.java 154(+0 -154)

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/BusinessInvoiceTagMapper.java 34(+0 -34)

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

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/DefaultAnalyticsDao.java 110(+0 -110)

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

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

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

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

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

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/BusinessSubscriptionTransitionModelDao.java 200(+0 -200)

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/PaymentMethodUtils.java 60(+0 -60)

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

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/AnalyticsSanitySqlDao.sql.stg 301(+0 -301)

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessAccountFieldSqlDao.sql.stg 42(+0 -42)

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessAccountSqlDao.sql.stg 123(+0 -123)

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessAccountTagSqlDao.sql.stg 39(+0 -39)

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceFieldSqlDao.sql.stg 39(+0 -39)

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceItemSqlDao.sql.stg 141(+0 -141)

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoicePaymentFieldSqlDao.sql.stg 39(+0 -39)

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoicePaymentSqlDao.sql.stg 122(+0 -122)

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoicePaymentTagSqlDao.sql.stg 37(+0 -37)

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceSqlDao.sql.stg 138(+0 -138)

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceTagSqlDao.sql.stg 37(+0 -37)

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessOverdueStatusSqlDao.sql.stg 50(+0 -50)

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessSubscriptionTransitionFieldSqlDao.sql.stg 46(+0 -46)

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessSubscriptionTransitionSqlDao.sql.stg 229(+0 -229)

osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessSubscriptionTransitionTagSqlDao.sql.stg 43(+0 -43)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestAnalyticsService.java 231(+0 -231)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessAccountFieldSqlDao.java 90(+0 -90)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessAccountTagSqlDao.java 88(+0 -88)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessInvoiceFieldSqlDao.java 86(+0 -86)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessInvoiceItemSqlDao.java 117(+0 -117)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessInvoicePaymentFieldSqlDao.java 86(+0 -86)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessInvoicePaymentSqlDao.java 118(+0 -118)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessInvoicePaymentTagSqlDao.java 84(+0 -84)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessInvoiceSqlDao.java 107(+0 -107)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessInvoiceTagSqlDao.java 84(+0 -84)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessOverdueStatusSqlDao.java 75(+0 -75)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessSubscriptionTransitionFieldSqlDao.java 92(+0 -92)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessSubscriptionTransitionTagSqlDao.java 90(+0 -90)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/glue/TestAnalyticsModule.java 68(+0 -68)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/glue/TestAnalyticsModuleNoDB.java 74(+0 -74)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/MockBusinessSubscriptionTransitionSqlDao.java 127(+0 -127)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/MockDuration.java 50(+0 -50)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/MockPhase.java 124(+0 -124)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/MockProduct.java 74(+0 -74)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/model/TestBusinessAccount.java 67(+0 -67)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/model/TestBusinessAccountField.java 52(+0 -52)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/model/TestBusinessInvoice.java 70(+0 -70)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/model/TestBusinessInvoiceField.java 48(+0 -48)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/model/TestBusinessInvoiceItem.java 80(+0 -80)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/model/TestBusinessInvoicePayment.java 86(+0 -86)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/model/TestBusinessInvoicePaymentField.java 48(+0 -48)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/model/TestBusinessOverdueStatus.java 57(+0 -57)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/model/TestBusinessSubscriptionEvent.java 124(+0 -124)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/model/TestBusinessSubscriptionTransition.java 161(+0 -161)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/model/TestBusinessSubscriptionTransitionField.java 56(+0 -56)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/model/TestBusinessSubscriptionTransitionTag.java 52(+0 -52)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/TestBusinessInvoiceRecorder.java 64(+0 -64)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/TestBusinessSubscriptionTransitionRecorder.java 87(+0 -87)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/TestBusinessTagRecorder.java 78(+0 -78)

osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/TestPaymentMethodUtils.java 56(+0 -56)

pom.xml 21(+21 -0)

Details

diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountFieldSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountFieldSqlDao.sql.stg
index 3715ed9..59f9318 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountFieldSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountFieldSqlDao.sql.stg
@@ -9,14 +9,14 @@ select
 , account_key
 , name
 , value
-from bac_fields
+from old_bac_fields
 where account_key = :account_key
 <AND_CHECK_TENANT()>
 ;
 >>
 
 addField(account_id, account_key, name, value) ::=<<
-insert into bac_fields (
+insert into old_bac_fields (
   account_id
 , account_key
 , name
@@ -34,9 +34,9 @@ insert into bac_fields (
 >>
 
 removeField(account_id, name) ::= <<
-delete from bac_fields where account_id = :account_id and name = :name <AND_CHECK_TENANT()>;
+delete from old_bac_fields where account_id = :account_id and name = :name <AND_CHECK_TENANT()>;
 >>
 
 test() ::= <<
-select 1 from bac_fields where <CHECK_TENANT()> limit 1;
+select 1 from old_bac_fields where <CHECK_TENANT()> limit 1;
 >>
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountSqlDao.sql.stg
index 819a5c9..1329899 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountSqlDao.sql.stg
@@ -8,7 +8,7 @@ getAccountsCreatedOverTime() ::= <<
     date(from_unixtime(created_date / 1000)) day
     -- TODO: use account_record_id, once populated
   , count(record_id) count
-  from bac
+  from old_bac
   where <CHECK_TENANT()>
   group by 1
   order by 1
@@ -31,7 +31,7 @@ getAccount(account_id) ::= <<
   , billing_address_country
   , currency
   , tenant_record_id
-  from bac
+  from old_bac
   where account_id=:account_id
   <AND_CHECK_TENANT()>
   limit 1
@@ -54,7 +54,7 @@ getAccountByKey(account_key) ::= <<
   , billing_address_country
   , currency
   , tenant_record_id
-  from bac
+  from old_bac
   where account_key=:account_key
   <AND_CHECK_TENANT()>
   limit 1
@@ -62,7 +62,7 @@ getAccountByKey(account_key) ::= <<
 >>
 
 createAccount() ::= <<
-  insert into bac(
+  insert into old_bac(
     account_id
   , account_key
   , created_date
@@ -98,7 +98,7 @@ createAccount() ::= <<
 >>
 
 saveAccount() ::= <<
-  update bac set
+  update old_bac set
     updated_date=:updated_date
   , balance=:balance
   , name=:name
@@ -115,9 +115,9 @@ saveAccount() ::= <<
 >>
 
 deleteAccount(account_id) ::= <<
-delete from bac where account_id = :account_id <AND_CHECK_TENANT()>;
+delete from old_bac where account_id = :account_id <AND_CHECK_TENANT()>;
 >>
 
 test() ::= <<
-select 1 from bac where <CHECK_TENANT()> limit 1;
+select 1 from old_bac where <CHECK_TENANT()> limit 1;
 >>
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountTagSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountTagSqlDao.sql.stg
index 5c4d142..91e2e0b 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountTagSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountTagSqlDao.sql.stg
@@ -8,14 +8,14 @@ select
   account_id
 , account_key
 , name
-from bac_tags
+from old_bac_tags
 where account_key = :account_key
 <AND_CHECK_TENANT()>
 ;
 >>
 
 addTag(account_id, account_key, name) ::=<<
-insert into bac_tags (
+insert into old_bac_tags (
   account_id
 , account_key
 , name
@@ -31,9 +31,9 @@ insert into bac_tags (
 >>
 
 removeTag(account_id, name) ::= <<
-delete from bac_tags where account_id = :account_id and name = :name <AND_CHECK_TENANT()>;
+delete from old_bac_tags where account_id = :account_id and name = :name <AND_CHECK_TENANT()>;
 >>
 
 test() ::= <<
-select 1 from bac_tags where <CHECK_TENANT()> limit 1;
+select 1 from old_bac_tags where <CHECK_TENANT()> limit 1;
 >>
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceFieldSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceFieldSqlDao.sql.stg
index 63e1ce0..703f678 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceFieldSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceFieldSqlDao.sql.stg
@@ -8,14 +8,14 @@ select
   invoice_id
 , name
 , value
-from bin_fields
+from old_bin_fields
 where invoice_id = :invoice_id
 <AND_CHECK_TENANT()>
 ;
 >>
 
 addField(invoice_id, name, value) ::=<<
-insert into bin_fields (
+insert into old_bin_fields (
   invoice_id
 , name
 , value
@@ -31,9 +31,9 @@ insert into bin_fields (
 >>
 
 removeField(invoice_id, name, value) ::= <<
-delete from bin_fields where invoice_id = :invoice_id and name = :name <AND_CHECK_TENANT()>;
+delete from old_bin_fields where invoice_id = :invoice_id and name = :name <AND_CHECK_TENANT()>;
 >>
 
 test() ::= <<
-select 1 from bin_tags where <CHECK_TENANT()> limit 1;
+select 1 from old_bin_tags where <CHECK_TENANT()> limit 1;
 >>
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceItemSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceItemSqlDao.sql.stg
index c15d21c..3a5bd22 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceItemSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceItemSqlDao.sql.stg
@@ -23,7 +23,7 @@ select
 , currency
 , linked_item_id
 , tenant_record_id
-from bii
+from old_bii
 where item_id = :item_id
 <AND_CHECK_TENANT()>
 limit 1
@@ -50,7 +50,7 @@ select
 , currency
 , linked_item_id
 , tenant_record_id
-from bii
+from old_bii
 where invoice_id = :invoice_id
 <AND_CHECK_TENANT()>
 order by created_date asc
@@ -77,7 +77,7 @@ select
 , currency
 , linked_item_id
 , tenant_record_id
-from bii
+from old_bii
 where external_key = :external_key
 <AND_CHECK_TENANT()>
 order by created_date asc
@@ -85,7 +85,7 @@ order by created_date asc
 >>
 
 createInvoiceItem() ::= <<
-insert into bii (
+insert into old_bii (
   item_id
 , created_date
 , updated_date
@@ -129,13 +129,13 @@ insert into bii (
 >>
 
 deleteInvoiceItem(item_id) ::= <<
-delete from bii where item_id = :item_id <AND_CHECK_TENANT()>;
+delete from old_bii where item_id = :item_id <AND_CHECK_TENANT()>;
 >>
 
 deleteInvoiceItemsForAccount(account_id) ::= <<
-delete from bii where bii.invoice_id in (select invoice_id from bin where bin.account_id = :account_id <AND_CHECK_TENANT("bin.")> for update) <AND_CHECK_TENANT("bii.")>;
+delete from old_bii where bii.invoice_id in (select invoice_id from bin where bin.account_id = :account_id <AND_CHECK_TENANT("bin.")> for update) <AND_CHECK_TENANT("bii.")>;
 >>
 
 test() ::= <<
-select 1 from bii where <CHECK_TENANT()> limit 1;
+select 1 from old_bii where <CHECK_TENANT()> limit 1;
 >>
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentFieldSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentFieldSqlDao.sql.stg
index 9bee903..723962c 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentFieldSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentFieldSqlDao.sql.stg
@@ -8,14 +8,14 @@ select
   payment_id
 , name
 , value
-from bip_fields
+from old_bip_fields
 where payment_id = :payment_id
 <AND_CHECK_TENANT()>
 ;
 >>
 
 addField(payment_id, name, value) ::=<<
-insert into bip_fields (
+insert into old_bip_fields (
   payment_id
 , name
 , value
@@ -31,9 +31,9 @@ insert into bip_fields (
 >>
 
 removeField(payment_id, name) ::= <<
-delete from bip_fields where payment_id = :payment_id and name = :name <AND_CHECK_TENANT()>;
+delete from old_bip_fields where payment_id = :payment_id and name = :name <AND_CHECK_TENANT()>;
 >>
 
 test() ::= <<
-select 1 from bip_fields where <CHECK_TENANT()> limit 1;
+select 1 from old_bip_fields where <CHECK_TENANT()> limit 1;
 >>
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentSqlDao.sql.stg
index e358c09..9b680de 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentSqlDao.sql.stg
@@ -26,7 +26,7 @@ select
 , invoice_payment_type
 , linked_invoice_payment_id
 , tenant_record_id
-from bip
+from old_bip
 where payment_id = :payment_id
 <AND_CHECK_TENANT()>
 limit 1
@@ -56,7 +56,7 @@ select
 , invoice_payment_type
 , linked_invoice_payment_id
 , tenant_record_id
-from bip
+from old_bip
 where account_key = :account_key
 <AND_CHECK_TENANT()>
 order by created_date asc
@@ -64,7 +64,7 @@ order by created_date asc
 >>
 
 createInvoicePayment() ::= <<
-insert into bip (
+insert into old_bip (
   payment_id
 , created_date
 , updated_date
@@ -114,9 +114,9 @@ insert into bip (
 >>
 
 deleteInvoicePayment(payment_id) ::= <<
-delete from bip where payment_id = :payment_id <AND_CHECK_TENANT()>
+delete from old_bip where payment_id = :payment_id <AND_CHECK_TENANT()>
 >>
 
 test() ::= <<
-select 1 from bip where <CHECK_TENANT()> limit 1;
+select 1 from old_bip where <CHECK_TENANT()> limit 1;
 >>
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentTagSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentTagSqlDao.sql.stg
index 541a809..9740ce4 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentTagSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoicePaymentTagSqlDao.sql.stg
@@ -8,14 +8,14 @@ select
   payment_id
 , name
 , tenant_record_id
-from bip_tags
+from old_bip_tags
 where payment_id = :payment_id
 <AND_CHECK_TENANT()>
 ;
 >>
 
 addTag(payment_id, name) ::=<<
-insert into bip_tags (
+insert into old_bip_tags (
   payment_id
 , name
 , account_record_id
@@ -29,9 +29,9 @@ insert into bip_tags (
 >>
 
 removeTag(payment_id, name) ::= <<
-delete from bip_tags where payment_id = :payment_id and name = :name <AND_CHECK_TENANT()>;
+delete from old_bip_tags where payment_id = :payment_id and name = :name <AND_CHECK_TENANT()>;
 >>
 
 test() ::= <<
-select 1 from bip_tags where <CHECK_TENANT()> limit 1;
+select 1 from old_bip_tags where <CHECK_TENANT()> limit 1;
 >>
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceSqlDao.sql.stg
index 2d19030..ba12ac3 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceSqlDao.sql.stg
@@ -19,7 +19,7 @@ select
 , amount_charged
 , amount_credited
 , tenant_record_id
-from bin
+from old_bin
 where invoice_id = :invoice_id
 <AND_CHECK_TENANT()>
 limit 1
@@ -42,7 +42,7 @@ select
 , amount_charged
 , amount_credited
 , tenant_record_id
-from bin
+from old_bin
 where account_id = :account_id
 <AND_CHECK_TENANT()>
 order by created_date asc
@@ -65,7 +65,7 @@ select
 , amount_charged
 , amount_credited
 , tenant_record_id
-from bin
+from old_bin
 where account_key = :account_key
 <AND_CHECK_TENANT()>
 order by created_date asc
@@ -73,7 +73,7 @@ order by created_date asc
 >>
 
 createInvoice() ::= <<
-insert into bin (
+insert into old_bin (
   invoice_id
 , invoice_number
 , created_date
@@ -109,7 +109,7 @@ insert into bin (
 >>
 
 updateInvoice() ::= <<
-update bin set
+update old_bin set
   updated_date = :updated_date
 , invoice_number = :invoice_number
 , account_key = :account_key
@@ -126,13 +126,13 @@ where invoice_id = :invoice_id
 >>
 
 deleteInvoice(invoice_id) ::= <<
-delete from bin where invoice_id = :invoice_id <AND_CHECK_TENANT()>;
+delete from old_bin where invoice_id = :invoice_id <AND_CHECK_TENANT()>;
 >>
 
 deleteInvoicesForAccount(account_id) ::= <<
-delete from bin where account_id = :account_id <AND_CHECK_TENANT()>;
+delete from old_bin where account_id = :account_id <AND_CHECK_TENANT()>;
 >>
 
 test() ::= <<
-select 1 from bin where <CHECK_TENANT()> limit 1;
+select 1 from old_bin where <CHECK_TENANT()> limit 1;
 >>
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceTagSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceTagSqlDao.sql.stg
index b2f5c0b..a357ea4 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceTagSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceTagSqlDao.sql.stg
@@ -8,14 +8,14 @@ select
   invoice_id
 , name
 , tenant_record_id
-from bin_tags
+from old_bin_tags
 where invoice_id = :invoice_id
 <AND_CHECK_TENANT()>
 ;
 >>
 
 addTag(invoice_id, name) ::=<<
-insert into bin_tags (
+insert into old_bin_tags (
   invoice_id
 , name
 , account_record_id
@@ -29,9 +29,9 @@ insert into bin_tags (
 >>
 
 removeTag(invoice_id, name) ::= <<
-delete from bin_tags where invoice_id = :invoice_id and name = :name <AND_CHECK_TENANT()>;
+delete from old_bin_tags where invoice_id = :invoice_id and name = :name <AND_CHECK_TENANT()>;
 >>
 
 test() ::= <<
-select 1 from bin_tags where <CHECK_TENANT()> limit 1;
+select 1 from old_bin_tags where <CHECK_TENANT()> limit 1;
 >>
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessOverdueStatusSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessOverdueStatusSqlDao.sql.stg
index 59eb814..941fab2 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessOverdueStatusSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessOverdueStatusSqlDao.sql.stg
@@ -12,7 +12,7 @@ select
 , start_date
 , end_date
 , tenant_record_id
-from bos
+from old_bos
 where external_key = :external_key
 <AND_CHECK_TENANT()>
 order by start_date asc
@@ -20,7 +20,7 @@ order by start_date asc
 >>
 
 createOverdueStatus() ::= <<
-insert into bos (
+insert into old_bos (
   bundle_id
 , external_key
 , account_key
@@ -42,9 +42,9 @@ insert into bos (
 >>
 
 deleteOverdueStatusesForBundle(bundle_id) ::= <<
-delete from bos where bundle_id = :bundle_id <AND_CHECK_TENANT()>;
+delete from old_bos where bundle_id = :bundle_id <AND_CHECK_TENANT()>;
 >>
 
 test() ::= <<
-select 1 from bos where <CHECK_TENANT()> limit 1;
+select 1 from old_bos where <CHECK_TENANT()> limit 1;
 >>
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionFieldSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionFieldSqlDao.sql.stg
index 3528475..b0c87fe 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionFieldSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionFieldSqlDao.sql.stg
@@ -11,14 +11,14 @@ select
 , name
 , value
 , tenant_record_id
-from bst_fields
+from old_bst_fields
 where external_key = :external_key
 <AND_CHECK_TENANT()>
 ;
 >>
 
 addField(bundle_id, external_key, name, value) ::=<<
-insert into bst_fields (
+insert into old_bst_fields (
   bundle_id
 , external_key
 , account_key
@@ -38,9 +38,9 @@ insert into bst_fields (
 >>
 
 removeField(bundle_id, name) ::= <<
-delete from bst_fields where bundle_id = :bundle_id and name = :name <AND_CHECK_TENANT()>;
+delete from old_bst_fields where bundle_id = :bundle_id and name = :name <AND_CHECK_TENANT()>;
 >>
 
 test() ::= <<
-select 1 from bst_fields where <CHECK_TENANT()> limit 1;
+select 1 from old_bst_fields where <CHECK_TENANT()> limit 1;
 >>
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionSqlDao.sql.stg
index d88006e..92d6b18 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionSqlDao.sql.stg
@@ -7,7 +7,7 @@ getSubscriptionsCreatedOverTime(product_type, slug) ::= <<
   select
     date(from_unixtime(requested_timestamp / 1000)) day
   , count(record_id) count
-  from bst
+  from old_bst
   where event in ('ADD_ADD_ON', 'ADD_BASE', 'ADD_STANDALONE')
   and next_product_type = :product_type
   and next_slug = :slug
@@ -52,7 +52,7 @@ getTransitionsByKey(external_key) ::= <<
   , next_start_date
   , next_state
   , tenant_record_id
-  from bst
+  from old_bst
   where external_key=:external_key
   <AND_CHECK_TENANT()>
   order by requested_timestamp asc
@@ -94,7 +94,7 @@ getTransitionForSubscription(subscription_id) ::= <<
   , next_start_date
   , next_state
   , tenant_record_id
-  from bst
+  from old_bst
   where subscription_id = :subscription_id
   <AND_CHECK_TENANT()>
   order by requested_timestamp asc
@@ -136,7 +136,7 @@ getTransitionsForAccount(account_id) ::= <<
   , next_start_date
   , next_state
   , tenant_record_id
-  from bst
+  from old_bst
   where account_key = :account_key
   <AND_CHECK_TENANT()>
   order by requested_timestamp asc
@@ -144,7 +144,7 @@ getTransitionsForAccount(account_id) ::= <<
 >>
 
 createTransition() ::= <<
-  insert into bst(
+  insert into old_bst(
     total_ordering
   , bundle_id
   , external_key
@@ -218,12 +218,12 @@ createTransition() ::= <<
 >>
 
 deleteTransitionsForBundle(bundle_id) ::= <<
-  delete from bst
+  delete from old_bst
   where bundle_id=:bundle_id
   <AND_CHECK_TENANT()>
   ;
 >>
 
 test() ::= <<
-select 1 from bst where <CHECK_TENANT()> limit 1;
+select 1 from old_bst where <CHECK_TENANT()> limit 1;
 >>
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionTagSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionTagSqlDao.sql.stg
index bd89b0d..621f950 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionTagSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionTagSqlDao.sql.stg
@@ -10,14 +10,14 @@ select
 , account_key
 , name
 , tenant_record_id
-from bst_tags
+from old_bst_tags
 where external_key = :external_key
 <AND_CHECK_TENANT()>
 ;
 >>
 
 addTag(bundle_id, external_key, name) ::=<<
-insert into bst_tags (
+insert into old_bst_tags (
   bundle_id
 , external_key
 , account_key
@@ -35,9 +35,9 @@ insert into bst_tags (
 >>
 
 removeTag(bundle_id, name) ::= <<
-delete from bst_tags where bundle_id = :bundle_id and name = :name <AND_CHECK_TENANT()>;
+delete from old_bst_tags where bundle_id = :bundle_id and name = :name <AND_CHECK_TENANT()>;
 >>
 
 test() ::= <<
-select 1 from bst_tags where <CHECK_TENANT()> limit 1;
+select 1 from old_bst_tags where <CHECK_TENANT()> limit 1;
 >>
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/ddl.sql b/analytics/src/main/resources/com/ning/billing/analytics/ddl.sql
index 7c93a84..8324289 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/ddl.sql
+++ b/analytics/src/main/resources/com/ning/billing/analytics/ddl.sql
@@ -1,7 +1,7 @@
 /*! SET storage_engine=INNODB */;
 
-drop table if exists bst;
-create table bst (
+drop table if exists old_bst;
+create table old_bst (
   record_id int(11) unsigned not null auto_increment
 , total_ordering bigint default 0
 , bundle_id char(36) not null
@@ -39,11 +39,11 @@ create table bst (
 , 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_tenant_account_record_id on bst(tenant_record_id, account_record_id);
+create index old_bst_key_index on old_bst (external_key, requested_timestamp asc);
+create index old_bst_tenant_account_record_id on old_bst(tenant_record_id, account_record_id);
 
-drop table if exists bac;
-create table bac (
+drop table if exists old_bac;
+create table old_bac (
   record_id int(11) unsigned not null auto_increment
 , account_id char(36) not null
 , account_key varchar(50) not null
@@ -62,11 +62,11 @@ create table bac (
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create unique index bac_key_index on bac (account_key);
-create index bac_tenant_account_record_id on bac(tenant_record_id, account_record_id);
+create unique index old_bac_key_index on old_bac (account_key);
+create index old_bac_tenant_account_record_id on old_bac(tenant_record_id, account_record_id);
 
-drop table if exists bin;
-create table bin (
+drop table if exists old_bin;
+create table old_bin (
   record_id int(11) unsigned not null auto_increment
 , invoice_id char(36) not null
 , invoice_number bigint default null
@@ -85,11 +85,11 @@ create table bin (
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create unique index bin_key_index on bin (invoice_id);
-create index bin_tenant_account_record_id on bin(tenant_record_id, account_record_id);
+create unique index old_bin_key_index on old_bin (invoice_id);
+create index old_bin_tenant_account_record_id on old_bin(tenant_record_id, account_record_id);
 
-drop table if exists bii;
-create table bii (
+drop table if exists old_bii;
+create table old_bii (
   record_id int(11) unsigned not null auto_increment
 , item_id char(36) not null
 , created_date bigint not null
@@ -112,11 +112,11 @@ create table bii (
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create unique index bii_key_index on bii (item_id);
-create index bii_tenant_account_record_id on bii(tenant_record_id, account_record_id);
+create unique index old_bii_key_index on old_bii (item_id);
+create index old_bii_tenant_account_record_id on old_bii(tenant_record_id, account_record_id);
 
-drop table if exists bip;
-create table bip (
+drop table if exists old_bip;
+create table old_bip (
   record_id int(11) unsigned not null auto_increment
 , payment_id char(36) not null
 , created_date bigint not null
@@ -142,11 +142,11 @@ create table bip (
 , 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 old_bip_key_index on old_bip (payment_id);
+create index old_bip_tenant_account_record_id on old_bip(tenant_record_id, account_record_id);
 
-drop table if exists bos;
-create table bos (
+drop table if exists old_bos;
+create table old_bos (
   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'
@@ -158,10 +158,10 @@ create table bos (
 , 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 index old_bos_tenant_account_record_id on old_bos(tenant_record_id, account_record_id);
 
-drop table if exists bac_tags;
-create table bac_tags (
+drop table if exists old_bac_tags;
+create table old_bac_tags (
   record_id int(11) unsigned not null auto_increment
 , account_id char(36) not null
 , account_key varchar(50) not null comment 'Account external key'
@@ -170,10 +170,10 @@ create table bac_tags (
 , 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 index old_bac_tags_tenant_account_record_id on old_bac_tags(tenant_record_id, account_record_id);
 
-drop table if exists bac_fields;
-create table bac_fields (
+drop table if exists old_bac_fields;
+create table old_bac_fields (
   record_id int(11) unsigned not null auto_increment
 , account_id char(36) not null
 , account_key varchar(50) not null comment 'Account external key'
@@ -183,10 +183,10 @@ create table bac_fields (
 , 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 index old_bac_fields_tenant_account_record_id on old_bac_fields(tenant_record_id, account_record_id);
 
-drop table if exists bst_tags;
-create table bst_tags (
+drop table if exists old_bst_tags;
+create table old_bst_tags (
   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'
@@ -196,10 +196,10 @@ create table bst_tags (
 , 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 index old_bst_tags_tenant_account_record_id on old_bst_tags(tenant_record_id, account_record_id);
 
-drop table if exists bst_fields;
-create table bst_fields (
+drop table if exists old_bst_fields;
+create table old_bst_fields (
   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'
@@ -210,10 +210,10 @@ create table bst_fields (
 , 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 old_bst_fields_tenant_account_record_id on old_bst_fields(tenant_record_id, account_record_id);
 
-drop table if exists bin_tags;
-create table bin_tags (
+drop table if exists old_bin_tags;
+create table old_bin_tags (
   record_id int(11) unsigned not null auto_increment
 , invoice_id char(36) not null
 , name varchar(50) not null
@@ -221,10 +221,10 @@ create table bin_tags (
 , 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);
+create index old_bin_tags_tenant_account_record_id on old_bin_tags(tenant_record_id, account_record_id);
 
-drop table if exists bin_fields;
-create table bin_fields (
+drop table if exists old_bin_fields;
+create table old_bin_fields (
   record_id int(11) unsigned not null auto_increment
 , invoice_id char(36) not null
 , name varchar(50) not null
@@ -233,10 +233,10 @@ create table bin_fields (
 , 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 old_bin_fields_tenant_account_record_id on old_bin_fields(tenant_record_id, account_record_id);
 
-drop table if exists bip_tags;
-create table bip_tags (
+drop table if exists old_bip_tags;
+create table old_bip_tags (
   record_id int(11) unsigned not null auto_increment
 , payment_id char(36) not null
 , name varchar(50) not null
@@ -244,10 +244,10 @@ create table bip_tags (
 , 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 old_bip_tags_tenant_account_record_id on old_bip_tags(tenant_record_id, account_record_id);
 
-drop table if exists bip_fields;
-create table bip_fields (
+drop table if exists old_bip_fields;
+create table old_bip_fields (
   record_id int(11) unsigned not null auto_increment
 , payment_id char(36) not null
 , name varchar(50) not null
@@ -256,4 +256,4 @@ create table bip_fields (
 , tenant_record_id int(11) unsigned default null
 , primary key(record_id)
 );
-create index bip_fields_tenant_account_record_id on bip_fields(tenant_record_id, account_record_id);
+create index old_bip_fields_tenant_account_record_id on old_bip_fields(tenant_record_id, account_record_id);
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java b/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java
index ceada44..0428928 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoicePayment.java
@@ -25,20 +25,50 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.entity.Entity;
 
 public interface InvoicePayment extends Entity {
+
+    /**
+     * @return payment id
+     */
     UUID getPaymentId();
 
+    /**
+     * @return invoice payment type
+     */
     InvoicePaymentType getType();
 
+    /**
+     * @return invoice id
+     */
     UUID getInvoiceId();
 
+    /**
+     * @return payment date
+     */
     DateTime getPaymentDate();
 
+    /**
+     * @return amount (from the payment)
+     */
     BigDecimal getAmount();
 
+    /**
+     * @return currency (from the payment)
+     */
     Currency getCurrency();
 
+    /**
+     * Linked invoice payment id: null for payments, associated
+     * invoice payment id for refunds and chargebacks
+     *
+     * @return linked invoice payment id
+     */
     UUID getLinkedInvoicePaymentId();
 
+    /**
+     * Payment cookie id: null for payments and chargebacks, refund id for refunds
+     *
+     * @return payment cookie id
+     */
     UUID getPaymentCookieId();
 
     public enum InvoicePaymentType {
diff --git a/api/src/main/java/com/ning/billing/ObjectType.java b/api/src/main/java/com/ning/billing/ObjectType.java
index 904f15e..0db2faa 100644
--- a/api/src/main/java/com/ning/billing/ObjectType.java
+++ b/api/src/main/java/com/ning/billing/ObjectType.java
@@ -19,6 +19,7 @@ package com.ning.billing;
 public enum ObjectType {
     ACCOUNT("account"),
     ACCOUNT_EMAIL("account email"),
+    BLOCKING_STATES("blocking state"),
     BUNDLE("subscription bundle"),
     CUSTOM_FIELD("custom field"),
     INVOICE("invoice"),
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultCatalogService.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultCatalogService.java
index c7114da..73c2ab4 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultCatalogService.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultCatalogService.java
@@ -16,19 +16,19 @@
 
 package com.ning.billing.catalog;
 
-import com.google.inject.Inject;
-import com.google.inject.Provider;
 import com.ning.billing.catalog.api.Catalog;
 import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.catalog.api.StaticCatalog;
 import com.ning.billing.catalog.io.VersionedCatalogLoader;
-import com.ning.billing.util.config.CatalogConfig;
 import com.ning.billing.lifecycle.KillbillService;
 import com.ning.billing.lifecycle.LifecycleHandlerType;
 import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
+import com.ning.billing.util.config.CatalogConfig;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
 
 public class DefaultCatalogService implements KillbillService, Provider<Catalog>, CatalogService {
-    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultCatalogService.class);
 
     private static final String CATALOG_SERVICE_NAME = "catalog-service";
 
@@ -39,7 +39,6 @@ public class DefaultCatalogService implements KillbillService, Provider<Catalog>
 
     private final VersionedCatalogLoader loader;
 
-
     @Inject
     public DefaultCatalogService(final CatalogConfig config, final VersionedCatalogLoader loader) {
         this.config = config;
@@ -61,23 +60,16 @@ public class DefaultCatalogService implements KillbillService, Provider<Catalog>
         }
     }
 
-
     @Override
     public String getName() {
         return CATALOG_SERVICE_NAME;
     }
 
-
-    /* (non-Javadoc)
-    * @see com.ning.billing.catalog.ICatlogService#getCatalog()
-    */
     @Override
     public Catalog getFullCatalog() {
         return catalog;
     }
 
-
-    // Should be able to use bind(ICatalog.class).toProvider(CatalogService.class);
     @Override
     public Catalog get() {
         return catalog;
@@ -87,5 +79,4 @@ public class DefaultCatalogService implements KillbillService, Provider<Catalog>
     public StaticCatalog getCurrentCatalog() {
         return catalog;
     }
-
 }
diff --git a/osgi/src/main/java/com/ning/billing/osgi/glue/OSGIDataSourceProvider.java b/osgi/src/main/java/com/ning/billing/osgi/glue/OSGIDataSourceProvider.java
index 455ff51..8ea9758 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/glue/OSGIDataSourceProvider.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/glue/OSGIDataSourceProvider.java
@@ -18,17 +18,12 @@ package com.ning.billing.osgi.glue;
 
 import java.util.concurrent.TimeUnit;
 
-import javax.inject.Inject;
 import javax.inject.Provider;
 import javax.sql.DataSource;
 
 import org.skife.config.TimeSpan;
 import org.skife.jdbi.v2.DBI;
-import org.skife.jdbi.v2.TimingCollector;
 import org.skife.jdbi.v2.tweak.SQLLog;
-import org.skife.jdbi.v2.tweak.TransactionHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.ning.billing.util.dao.DateTimeArgumentFactory;
 import com.ning.billing.util.dao.DateTimeZoneArgumentFactory;
@@ -37,30 +32,22 @@ import com.ning.billing.util.dao.LocalDateArgumentFactory;
 import com.ning.billing.util.dao.UUIDArgumentFactory;
 import com.ning.billing.util.dao.UuidMapper;
 
+import com.google.inject.Inject;
 import com.jolbox.bonecp.BoneCPConfig;
 import com.jolbox.bonecp.BoneCPDataSource;
 import com.mchange.v2.c3p0.ComboPooledDataSource;
-import com.yammer.metrics.core.MetricsRegistry;
 
 public class OSGIDataSourceProvider implements Provider<DataSource> {
 
     private final OSGIDataSourceConfig config;
-    private final String jdbcUri;
-    private final String userName;
-    private final String userPwd;
-
     private SQLLog sqlLog;
 
     @Inject
     public OSGIDataSourceProvider(final OSGIDataSourceConfig config) {
         this.config = config;
-        this.jdbcUri = config.getJdbcUrl();
-        this.userName = config.getUsername();
-        this.userPwd = config.getPassword();
     }
 
-
-    @com.google.inject.Inject(optional = true)
+    @Inject(optional = true)
     public void setSqlLog(final SQLLog sqlLog) {
         this.sqlLog = sqlLog;
     }
@@ -136,12 +123,12 @@ public class OSGIDataSourceProvider implements Provider<DataSource> {
         cpds.setCheckoutTimeout(toMilliSeconds(config.getConnectionTimeout()));
         // http://www.mchange.com/projects/c3p0/#maxIdleTime
         // Seconds a Connection can remain pooled but unused before being discarded. Zero means idle connections never expire.
-//        cpds.setMaxIdleTime(toSeconds(config.getIdleMaxAge()));
+        //        cpds.setMaxIdleTime(toSeconds(config.getIdleMaxAge()));
         // http://www.mchange.com/projects/c3p0/#maxConnectionAge
         // Seconds, effectively a time to live. A Connection older than maxConnectionAge will be destroyed and purged from the pool.
         // This differs from maxIdleTime in that it refers to absolute age. Even a Connection which has not been much idle will be purged
         // from the pool if it exceeds maxConnectionAge. Zero means no maximum absolute age is enforced.
-//        cpds.setMaxConnectionAge(toSeconds(config.getMaxConnectionAge()));
+        //        cpds.setMaxConnectionAge(toSeconds(config.getMaxConnectionAge()));
         // http://www.mchange.com/projects/c3p0/#idleConnectionTestPeriod
         // If this is a number greater than 0, c3p0 will test all idle, pooled but unchecked-out connections, every this number of seconds.
         cpds.setIdleConnectionTestPeriod(60);
diff --git a/osgi-bundles/bundles/analytics/pom.xml b/osgi-bundles/bundles/analytics/pom.xml
index 6b2da9c..791be57 100644
--- a/osgi-bundles/bundles/analytics/pom.xml
+++ b/osgi-bundles/bundles/analytics/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill-osgi-bundles</artifactId>
-        <version>0.1.62-SNAPSHOT</version>
+        <version>0.1.63-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-analytics</artifactId>
@@ -29,12 +29,17 @@
     <packaging>bundle</packaging>
     <dependencies>
         <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-joda</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.google.inject</groupId>
-            <artifactId>guice</artifactId>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <scope>compile</scope>
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
@@ -47,7 +52,19 @@
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-util</artifactId>
+            <artifactId>killbill-osgi-bundles-lib-slf4j-osgi</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>
@@ -90,62 +107,8 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-account</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-catalog</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-catalog</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-entitlement</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-entitlement</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-junction</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-junction</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-invoice</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-payment</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.ning.billing</groupId>
-            <artifactId>killbill-util</artifactId>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.jayway.awaitility</groupId>
-            <artifactId>awaitility</artifactId>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -158,16 +121,6 @@
             <artifactId>mysql-connector-mxj-db-files</artifactId>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>com.h2database</groupId>
-            <artifactId>h2</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-simple</artifactId>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
     <build>
         <plugins>
@@ -202,7 +155,7 @@
                         <Export-Package/>
                         <Private-Package>com.ning.billing.osgi.bundles.analytics.*</Private-Package>
                         <!-- Optional resolution because exported by the Felix system bundle -->
-                        <Import-Package>*;resolution:=optional</Import-Package>
+                        <Import-Package>*;com.ning.billing.osgi.api;resolution:=optional</Import-Package>
                     </instructions>
                 </configuration>
                 <executions>
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..abc42c0 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
@@ -16,31 +16,45 @@
 
 package com.ning.billing.osgi.bundles.analytics;
 
+import java.util.Hashtable;
+
+import javax.servlet.Servlet;
+import javax.servlet.http.HttpServlet;
+
 import org.osgi.framework.BundleContext;
-import org.skife.config.SimplePropertyConfigSource;
 
-import com.ning.billing.osgi.bundles.analytics.setup.AnalyticsModule;
+import com.ning.billing.osgi.api.OSGIPluginProperties;
+import com.ning.billing.osgi.bundles.analytics.api.user.AnalyticsUserApi;
+import com.ning.billing.osgi.bundles.analytics.http.AnalyticsServlet;
 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 {
 
+    public static final String PLUGIN_NAME = "killbill-analytics";
+
     private OSGIKillbillEventHandler analyticsListener;
 
     @Override
     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);
+        dispatcher.registerEventHandler(analyticsListener);
+
+        final AnalyticsUserApi analyticsUserApi = new AnalyticsUserApi(logService, killbillAPI, dataSource);
+        final AnalyticsServlet analyticsServlet = new AnalyticsServlet(analyticsUserApi);
+        registerServlet(context, analyticsServlet);
     }
 
     @Override
     public OSGIKillbillEventHandler getOSGIKillbillEventHandler() {
         return analyticsListener;
     }
+
+    private void registerServlet(final BundleContext context, final HttpServlet servlet) {
+        final Hashtable<String, String> props = new Hashtable<String, String>();
+        props.put(OSGIPluginProperties.PLUGIN_NAME_PROP, PLUGIN_NAME);
+        registrar.registerService(context, Servlet.class, servlet, props);
+    }
 }
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..c04962f 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,239 @@
 
 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.osgi.bundles.analytics.dao.BusinessAccountDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessFieldDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoicePaymentDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessOverdueStatusDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessSubscriptionTransitionDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessTagDao;
+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.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.google.inject.Inject;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
 
 public class AnalyticsListener implements OSGIKillbillEventHandler {
 
-    private static final Logger log = LoggerFactory.getLogger(AnalyticsListener.class);
-    private static final int NB_LOCK_TRY = 5;
+    private static final String ANALYTICS_NB_LOCK_TRY_PROPERTY = "killbill.osgi.analytics.lock.count";
+    private static final int NB_LOCK_TRY = Integer.parseInt(System.getProperty(ANALYTICS_NB_LOCK_TRY_PROPERTY, "5"));
 
-    private final BusinessSubscriptionTransitionDao bstDao;
+    private final LogService logService;
     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 BusinessFieldDao bFieldDao;
+    private final BusinessTagDao bTagDao;
     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) {
+        this.logService = logService;
+
+        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.bFieldDao = new BusinessFieldDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
+        this.bTagDao = new BusinessTagDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
+
+        this.locker = new MySqlGlobalLocker(osgiKillbillDataSource.getDataSource());
     }
 
     @Override
     public void handleKillbillEvent(final ExtBusEvent killbillEvent) {
-        // TODO
+        final CallContext callContext = new AnalyticsCallContext(killbillEvent);
+
+        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:
+            case INVOICE_ADJUSTMENT:
+                handleInvoiceEvent(killbillEvent, callContext);
+                break;
+            case PAYMENT_SUCCESS:
+            case PAYMENT_FAILED:
+                handlePaymentEvent(killbillEvent, callContext);
+                break;
+            case TAG_CREATION:
+            case TAG_DELETION:
+                handleTagEvent(killbillEvent, callContext);
+                break;
+            case CUSTOM_FIELD_CREATION:
+            case CUSTOM_FIELD_DELETION:
+                handleFieldEvent(killbillEvent, callContext);
+                break;
+            default:
+                break;
+        }
     }
 
-    private void handleEffectiveSubscriptionTransitionChange(final EffectiveSubscriptionInternalEvent eventEffective) {
-        updateWithAccountLock(eventEffective.getAccountRecordId(), new Callable<Void>() {
+    private void handleAccountEvent(final ExtBusEvent killbillEvent, final CallContext callContext) {
+        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(eventEffective.getBundleId(), createCallContext(eventEffective));
+                bacDao.update(killbillEvent.getAccountId(), callContext);
                 return null;
             }
         });
     }
 
-    private void handleRequestedSubscriptionTransitionChange(final RequestedSubscriptionInternalEvent eventRequested) {
-        updateWithAccountLock(eventRequested.getAccountRecordId(), new Callable<Void>() {
+    private void handleSubscriptionEvent(final ExtBusEvent killbillEvent, final CallContext callContext) {
+        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));
+                bstDao.update(killbillEvent.getAccountId(), callContext);
                 return null;
             }
         });
     }
 
-    private void handleRepairEntitlement(final RepairEntitlementInternalEvent event) {
-        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
+    private void handleInvoiceEvent(final ExtBusEvent killbillEvent, final CallContext 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));
+                binDao.update(killbillEvent.getAccountId(), callContext);
                 return null;
             }
         });
     }
 
-    private void handleAccountCreation(final AccountCreationInternalEvent event) {
-        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
+    private void handlePaymentEvent(final ExtBusEvent killbillEvent, final CallContext callContext) {
+        updateWithAccountLock(killbillEvent, new Callable<Void>() {
             @Override
             public Void call() throws Exception {
-                bacDao.accountUpdated(event.getId(), createCallContext(event));
+                bipDao.update(killbillEvent.getAccountId(), callContext);
                 return null;
             }
         });
     }
 
-    private void handleAccountChange(final AccountChangeInternalEvent event) {
-        if (!event.hasChanges()) {
-            return;
-        }
-
-        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
+    private void handleOverdueEvent(final ExtBusEvent killbillEvent, final CallContext callContext) {
+        updateWithAccountLock(killbillEvent, new Callable<Void>() {
             @Override
             public Void call() throws Exception {
-                bacDao.accountUpdated(event.getAccountId(), createCallContext(event));
+                bosDao.update(killbillEvent.getAccountId(), killbillEvent.getObjectType(), callContext);
                 return null;
             }
         });
     }
 
-    private void handleInvoiceCreation(final InvoiceCreationInternalEvent event) {
-        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
+    private void handleTagEvent(final ExtBusEvent killbillEvent, final CallContext callContext) {
+        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));
+                bTagDao.update(killbillEvent.getAccountId(), callContext);
                 return null;
             }
         });
     }
 
-    private void handleNullInvoice(final NullInvoiceInternalEvent event) {
-        // Ignored for now
-    }
-
-    private void handleInvoiceAdjustment(final InvoiceAdjustmentInternalEvent event) {
-        updateWithAccountLock(event.getAccountRecordId(), new Callable<Void>() {
+    private void handleFieldEvent(final ExtBusEvent killbillEvent, final CallContext callContext) {
+        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));
+                bFieldDao.update(killbillEvent.getAccountId(), callContext);
                 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));
-                return null;
-            }
-        });
-    }
+    private static final class AnalyticsCallContext implements CallContext {
 
-    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 String USER_NAME = AnalyticsListener.class.getName();
 
-    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 final ExtBusEvent killbillEvent;
+        private final DateTime now;
 
-    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 AnalyticsCallContext(final ExtBusEvent killbillEvent) {
+            this.killbillEvent = killbillEvent;
+            this.now = new DateTime(DateTimeZone.UTC);
+        }
 
-    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;
-            }
-        });
-    }
+        @Override
+        public UUID getUserToken() {
+            return UUID.randomUUID();
+        }
 
-    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 String getUserName() {
+            return USER_NAME;
+        }
 
-    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 CallOrigin getCallOrigin() {
+            return CallOrigin.INTERNAL;
+        }
 
-    private void handleControlTagDefinitionCreation(final ControlTagDefinitionCreationInternalEvent event) {
-        // Ignored for now
-    }
+        @Override
+        public UserType getUserType() {
+            return UserType.SYSTEM;
+        }
 
-    private void handleControlTagDefinitionDeletion(final ControlTagDefinitionDeletionInternalEvent event) {
-        // Ignored for now
-    }
+        @Override
+        public String getReasonCode() {
+            return killbillEvent.getEventType().toString();
+        }
 
-    private void handleUserTagDefinitionCreation(final UserTagDefinitionCreationInternalEvent 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 void handleUserTagDefinitionDeletion(final UserTagDefinitionDeletionInternalEvent event) {
-        // Ignored for now
-    }
+        @Override
+        public DateTime getCreatedDate() {
+            return now;
+        }
+
+        @Override
+        public DateTime getUpdatedDate() {
+            return now;
+        }
 
-    private InternalCallContext createCallContext(final BusInternalEvent event) {
-        return internalCallContextFactory.createInternalCallContext(event.getTenantRecordId(), event.getAccountRecordId(),
-                                                                    "AnalyticsService", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
+        @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..65686e9
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessAccount.java
@@ -0,0 +1,313 @@
+/*
+ * 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.osgi.bundles.analytics.dao.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 UUID paymentMethodId;
+    private final String timeZone;
+    private final String locale;
+    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 DateTime updatedDate;
+    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.paymentMethodId = businessAccountModelDao.getPaymentMethodId();
+        this.timeZone = businessAccountModelDao.getTimeZone();
+        this.locale = businessAccountModelDao.getLocale();
+        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.updatedDate = businessAccountModelDao.getUpdatedDate();
+        this.balance = businessAccountModelDao.getBalance();
+        this.lastInvoiceDate = businessAccountModelDao.getLastInvoiceDate();
+        this.lastPaymentDate = businessAccountModelDao.getLastPaymentDate();
+        this.lastPaymentStatus = businessAccountModelDao.getLastPaymentStatus();
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public Integer getFirstNameLength() {
+        return firstNameLength;
+    }
+
+    public String getCurrency() {
+        return currency;
+    }
+
+    public Integer getBillingCycleDayLocal() {
+        return billingCycleDayLocal;
+    }
+
+    public UUID getPaymentMethodId() {
+        return paymentMethodId;
+    }
+
+    public String getTimeZone() {
+        return timeZone;
+    }
+
+    public String getLocale() {
+        return locale;
+    }
+
+    public String getAddress1() {
+        return address1;
+    }
+
+    public String getAddress2() {
+        return address2;
+    }
+
+    public String getCompanyName() {
+        return companyName;
+    }
+
+    public String getCity() {
+        return city;
+    }
+
+    public String getStateOrProvince() {
+        return stateOrProvince;
+    }
+
+    public String getCountry() {
+        return country;
+    }
+
+    public String getPostalCode() {
+        return postalCode;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public Boolean getMigrated() {
+        return isMigrated;
+    }
+
+    public Boolean getNotifiedForInvoices() {
+        return isNotifiedForInvoices;
+    }
+
+    public DateTime getUpdatedDate() {
+        return updatedDate;
+    }
+
+    public BigDecimal getBalance() {
+        return balance;
+    }
+
+    public LocalDate getLastInvoiceDate() {
+        return lastInvoiceDate;
+    }
+
+    public DateTime getLastPaymentDate() {
+        return lastPaymentDate;
+    }
+
+    public String getLastPaymentStatus() {
+        return lastPaymentStatus;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessAccount");
+        sb.append("{email='").append(email).append('\'');
+        sb.append(", firstNameLength=").append(firstNameLength);
+        sb.append(", currency='").append(currency).append('\'');
+        sb.append(", billingCycleDayLocal=").append(billingCycleDayLocal);
+        sb.append(", paymentMethodId=").append(paymentMethodId);
+        sb.append(", timeZone='").append(timeZone).append('\'');
+        sb.append(", locale='").append(locale).append('\'');
+        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(", updatedDate=").append(updatedDate);
+        sb.append(", balance=").append(balance);
+        sb.append(", lastInvoiceDate=").append(lastInvoiceDate);
+        sb.append(", lastPaymentDate=").append(lastPaymentDate);
+        sb.append(", lastPaymentStatus='").append(lastPaymentStatus).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 BusinessAccount that = (BusinessAccount) o;
+
+        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 (balance != null ? balance.compareTo(that.balance) != 0 : that.balance != null) {
+            return false;
+        }
+        if (billingCycleDayLocal != null ? !billingCycleDayLocal.equals(that.billingCycleDayLocal) : that.billingCycleDayLocal != null) {
+            return false;
+        }
+        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 (email != null ? !email.equals(that.email) : that.email != null) {
+            return false;
+        }
+        if (firstNameLength != null ? !firstNameLength.equals(that.firstNameLength) : that.firstNameLength != null) {
+            return false;
+        }
+        if (isMigrated != null ? !isMigrated.equals(that.isMigrated) : that.isMigrated != null) {
+            return false;
+        }
+        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 (lastPaymentStatus != null ? !lastPaymentStatus.equals(that.lastPaymentStatus) : that.lastPaymentStatus != null) {
+            return false;
+        }
+        if (locale != null ? !locale.equals(that.locale) : that.locale != null) {
+            return false;
+        }
+        if (paymentMethodId != null ? !paymentMethodId.equals(that.paymentMethodId) : that.paymentMethodId != null) {
+            return false;
+        }
+        if (phone != null ? !phone.equals(that.phone) : that.phone != null) {
+            return false;
+        }
+        if (postalCode != null ? !postalCode.equals(that.postalCode) : that.postalCode != null) {
+            return false;
+        }
+        if (stateOrProvince != null ? !stateOrProvince.equals(that.stateOrProvince) : that.stateOrProvince != null) {
+            return false;
+        }
+        if (timeZone != null ? !timeZone.equals(that.timeZone) : that.timeZone != null) {
+            return false;
+        }
+        if (updatedDate != null ? !updatedDate.equals(that.updatedDate) : that.updatedDate != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        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 + (paymentMethodId != null ? paymentMethodId.hashCode() : 0);
+        result = 31 * result + (timeZone != null ? timeZone.hashCode() : 0);
+        result = 31 * result + (locale != null ? locale.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 + (updatedDate != null ? updatedDate.hashCode() : 0);
+        result = 31 * result + (balance != null ? balance.hashCode() : 0);
+        result = 31 * result + (lastInvoiceDate != null ? lastInvoiceDate.hashCode() : 0);
+        result = 31 * result + (lastPaymentDate != null ? lastPaymentDate.hashCode() : 0);
+        result = 31 * result + (lastPaymentStatus != null ? lastPaymentStatus.hashCode() : 0);
+        return result;
+    }
+}
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..f47415d
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessInvoiceItem.java
@@ -0,0 +1,357 @@
+/*
+ * 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.osgi.bundles.analytics.dao.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 String 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.getRevenueRecognizable();
+        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();
+    }
+
+    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 String getCurrency() {
+        return currency;
+    }
+
+    public UUID getLinkedItemId() {
+        return linkedItemId;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessInvoiceItem");
+        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).append('\'');
+        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 BusinessInvoiceItem that = (BusinessInvoiceItem) 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 != null ? !currency.equals(that.currency) : that.currency != null) {
+            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 (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 + (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/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..d5cd664
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessInvoicePayment.java
@@ -0,0 +1,258 @@
+/*
+ * 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.osgi.bundles.analytics.dao.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 String 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();
+    }
+
+    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 String getCurrency() {
+        return currency;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessInvoicePayment");
+        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).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 BusinessInvoicePayment that = (BusinessInvoicePayment) o;
+
+        if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
+            return false;
+        }
+        if (currency != null ? !currency.equals(that.currency) : that.currency != 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 (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 (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 + (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/api/BusinessSubscriptionTransition.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessSubscriptionTransition.java
new file mode 100644
index 0000000..4a1f6ed
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/BusinessSubscriptionTransition.java
@@ -0,0 +1,429 @@
+/*
+ * 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 com.ning.billing.osgi.bundles.analytics.dao.model.BusinessSubscriptionTransitionModelDao;
+
+public class BusinessSubscriptionTransition extends BusinessEntityBase {
+
+    private final UUID bundleId;
+    private final String bundleExternalKey;
+    private final UUID subscriptionId;
+
+    private final DateTime requestedTimestamp;
+    private final String event;
+
+    private final String prevProductName;
+    private final String prevProductType;
+    private final String prevProductCategory;
+    private final String prevSlug;
+    private final String prevPhase;
+    private final String prevBillingPeriod;
+    private final BigDecimal prevPrice;
+    private final String prevPriceList;
+    private final BigDecimal prevMrr;
+    private final String prevCurrency;
+    private final Boolean prevBusinessActive;
+    private final DateTime prevStartDate;
+    private final String prevState;
+
+    private final String nextProductName;
+    private final String nextProductType;
+    private final String nextProductCategory;
+    private final String nextSlug;
+    private final String nextPhase;
+    private final String nextBillingPeriod;
+    private final BigDecimal nextPrice;
+    private final String nextPriceList;
+    private final BigDecimal nextMrr;
+    private final String nextCurrency;
+    private final Boolean nextBusinessActive;
+    private final DateTime nextStartDate;
+    private final DateTime nextEndDate;
+    private final String nextState;
+
+    public BusinessSubscriptionTransition(final BusinessSubscriptionTransitionModelDao bstModelDao) {
+        super(bstModelDao.getCreatedDate(),
+              bstModelDao.getCreatedBy(),
+              bstModelDao.getCreatedReasonCode(),
+              bstModelDao.getCreatedComments(),
+              bstModelDao.getAccountId(),
+              bstModelDao.getAccountName(),
+              bstModelDao.getAccountExternalKey());
+
+        this.bundleId = bstModelDao.getBundleId();
+        this.bundleExternalKey = bstModelDao.getBundleExternalKey();
+        this.subscriptionId = bstModelDao.getSubscriptionId();
+
+        this.requestedTimestamp = bstModelDao.getRequestedTimestamp();
+        this.event = bstModelDao.getEvent();
+
+        this.prevProductName = bstModelDao.getPrevProductName();
+        this.prevProductType = bstModelDao.getPrevProductType();
+        this.prevProductCategory = bstModelDao.getPrevProductCategory();
+        this.prevSlug = bstModelDao.getPrevSlug();
+        this.prevPhase = bstModelDao.getPrevPhase();
+        this.prevBillingPeriod = bstModelDao.getPrevBillingPeriod();
+        this.prevPrice = bstModelDao.getPrevPrice();
+        this.prevPriceList = bstModelDao.getPrevPriceList();
+        this.prevMrr = bstModelDao.getPrevMrr();
+        this.prevCurrency = bstModelDao.getPrevCurrency();
+        this.prevBusinessActive = bstModelDao.getPrevBusinessActive();
+        this.prevStartDate = bstModelDao.getPrevStartDate();
+        this.prevState = bstModelDao.getPrevState();
+
+        this.nextProductName = bstModelDao.getNextProductName();
+        this.nextProductType = bstModelDao.getNextProductType();
+        this.nextProductCategory = bstModelDao.getNextProductCategory();
+        this.nextSlug = bstModelDao.getNextSlug();
+        this.nextPhase = bstModelDao.getNextPhase();
+        this.nextBillingPeriod = bstModelDao.getNextBillingPeriod();
+        this.nextPrice = bstModelDao.getNextPrice();
+        this.nextPriceList = bstModelDao.getNextPriceList();
+        this.nextMrr = bstModelDao.getNextMrr();
+        this.nextCurrency = bstModelDao.getNextCurrency();
+        this.nextBusinessActive = bstModelDao.getNextBusinessActive();
+        this.nextStartDate = bstModelDao.getNextStartDate();
+        this.nextEndDate = bstModelDao.getNextEndDate();
+        this.nextState = bstModelDao.getNextState();
+    }
+
+    public UUID getBundleId() {
+        return bundleId;
+    }
+
+    public String getBundleExternalKey() {
+        return bundleExternalKey;
+    }
+
+    public UUID getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    public DateTime getRequestedTimestamp() {
+        return requestedTimestamp;
+    }
+
+    public String getEvent() {
+        return event;
+    }
+
+    public String getPrevProductName() {
+        return prevProductName;
+    }
+
+    public String getPrevProductType() {
+        return prevProductType;
+    }
+
+    public String getPrevProductCategory() {
+        return prevProductCategory;
+    }
+
+    public String getPrevSlug() {
+        return prevSlug;
+    }
+
+    public String getPrevPhase() {
+        return prevPhase;
+    }
+
+    public String getPrevBillingPeriod() {
+        return prevBillingPeriod;
+    }
+
+    public BigDecimal getPrevPrice() {
+        return prevPrice;
+    }
+
+    public String getPrevPriceList() {
+        return prevPriceList;
+    }
+
+    public BigDecimal getPrevMrr() {
+        return prevMrr;
+    }
+
+    public String getPrevCurrency() {
+        return prevCurrency;
+    }
+
+    public Boolean getPrevBusinessActive() {
+        return prevBusinessActive;
+    }
+
+    public DateTime getPrevStartDate() {
+        return prevStartDate;
+    }
+
+    public String getPrevState() {
+        return prevState;
+    }
+
+    public String getNextProductName() {
+        return nextProductName;
+    }
+
+    public String getNextProductType() {
+        return nextProductType;
+    }
+
+    public String getNextProductCategory() {
+        return nextProductCategory;
+    }
+
+    public String getNextSlug() {
+        return nextSlug;
+    }
+
+    public String getNextPhase() {
+        return nextPhase;
+    }
+
+    public String getNextBillingPeriod() {
+        return nextBillingPeriod;
+    }
+
+    public BigDecimal getNextPrice() {
+        return nextPrice;
+    }
+
+    public String getNextPriceList() {
+        return nextPriceList;
+    }
+
+    public BigDecimal getNextMrr() {
+        return nextMrr;
+    }
+
+    public String getNextCurrency() {
+        return nextCurrency;
+    }
+
+    public Boolean getNextBusinessActive() {
+        return nextBusinessActive;
+    }
+
+    public DateTime getNextStartDate() {
+        return nextStartDate;
+    }
+
+    public DateTime getNextEndDate() {
+        return nextEndDate;
+    }
+
+    public String getNextState() {
+        return nextState;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessSubscriptionTransition");
+        sb.append("{bundleId=").append(bundleId);
+        sb.append(", bundleExternalKey='").append(bundleExternalKey).append('\'');
+        sb.append(", subscriptionId=").append(subscriptionId);
+        sb.append(", requestedTimestamp=").append(requestedTimestamp);
+        sb.append(", event='").append(event).append('\'');
+        sb.append(", prevProductName='").append(prevProductName).append('\'');
+        sb.append(", prevProductType='").append(prevProductType).append('\'');
+        sb.append(", prevProductCategory='").append(prevProductCategory).append('\'');
+        sb.append(", prevSlug='").append(prevSlug).append('\'');
+        sb.append(", prevPhase='").append(prevPhase).append('\'');
+        sb.append(", prevBillingPeriod='").append(prevBillingPeriod).append('\'');
+        sb.append(", prevPrice=").append(prevPrice);
+        sb.append(", prevPriceList='").append(prevPriceList).append('\'');
+        sb.append(", prevMrr=").append(prevMrr);
+        sb.append(", prevCurrency='").append(prevCurrency).append('\'');
+        sb.append(", prevBusinessActive=").append(prevBusinessActive);
+        sb.append(", prevStartDate=").append(prevStartDate);
+        sb.append(", prevState='").append(prevState).append('\'');
+        sb.append(", nextProductName='").append(nextProductName).append('\'');
+        sb.append(", nextProductType='").append(nextProductType).append('\'');
+        sb.append(", nextProductCategory='").append(nextProductCategory).append('\'');
+        sb.append(", nextSlug='").append(nextSlug).append('\'');
+        sb.append(", nextPhase='").append(nextPhase).append('\'');
+        sb.append(", nextBillingPeriod='").append(nextBillingPeriod).append('\'');
+        sb.append(", nextPrice=").append(nextPrice);
+        sb.append(", nextPriceList='").append(nextPriceList).append('\'');
+        sb.append(", nextMrr=").append(nextMrr);
+        sb.append(", nextCurrency='").append(nextCurrency).append('\'');
+        sb.append(", nextBusinessActive=").append(nextBusinessActive);
+        sb.append(", nextStartDate=").append(nextStartDate);
+        sb.append(", nextEndDate=").append(nextEndDate);
+        sb.append(", nextState='").append(nextState).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 BusinessSubscriptionTransition that = (BusinessSubscriptionTransition) o;
+
+        if (bundleExternalKey != null ? !bundleExternalKey.equals(that.bundleExternalKey) : that.bundleExternalKey != null) {
+            return false;
+        }
+        if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
+            return false;
+        }
+        if (event != null ? !event.equals(that.event) : that.event != null) {
+            return false;
+        }
+        if (nextBillingPeriod != null ? !nextBillingPeriod.equals(that.nextBillingPeriod) : that.nextBillingPeriod != null) {
+            return false;
+        }
+        if (nextBusinessActive != null ? !nextBusinessActive.equals(that.nextBusinessActive) : that.nextBusinessActive != null) {
+            return false;
+        }
+        if (nextCurrency != null ? !nextCurrency.equals(that.nextCurrency) : that.nextCurrency != null) {
+            return false;
+        }
+        if (nextEndDate != null ? !nextEndDate.equals(that.nextEndDate) : that.nextEndDate != null) {
+            return false;
+        }
+        if (nextMrr != null ? !nextMrr.equals(that.nextMrr) : that.nextMrr != null) {
+            return false;
+        }
+        if (nextPhase != null ? !nextPhase.equals(that.nextPhase) : that.nextPhase != null) {
+            return false;
+        }
+        if (nextPrice != null ? !nextPrice.equals(that.nextPrice) : that.nextPrice != null) {
+            return false;
+        }
+        if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
+            return false;
+        }
+        if (nextProductCategory != null ? !nextProductCategory.equals(that.nextProductCategory) : that.nextProductCategory != null) {
+            return false;
+        }
+        if (nextProductName != null ? !nextProductName.equals(that.nextProductName) : that.nextProductName != null) {
+            return false;
+        }
+        if (nextProductType != null ? !nextProductType.equals(that.nextProductType) : that.nextProductType != null) {
+            return false;
+        }
+        if (nextSlug != null ? !nextSlug.equals(that.nextSlug) : that.nextSlug != null) {
+            return false;
+        }
+        if (nextStartDate != null ? !nextStartDate.equals(that.nextStartDate) : that.nextStartDate != null) {
+            return false;
+        }
+        if (nextState != null ? !nextState.equals(that.nextState) : that.nextState != null) {
+            return false;
+        }
+        if (prevBillingPeriod != null ? !prevBillingPeriod.equals(that.prevBillingPeriod) : that.prevBillingPeriod != null) {
+            return false;
+        }
+        if (prevBusinessActive != null ? !prevBusinessActive.equals(that.prevBusinessActive) : that.prevBusinessActive != null) {
+            return false;
+        }
+        if (prevCurrency != null ? !prevCurrency.equals(that.prevCurrency) : that.prevCurrency != null) {
+            return false;
+        }
+        if (prevMrr != null ? !prevMrr.equals(that.prevMrr) : that.prevMrr != null) {
+            return false;
+        }
+        if (prevPhase != null ? !prevPhase.equals(that.prevPhase) : that.prevPhase != null) {
+            return false;
+        }
+        if (prevPrice != null ? !prevPrice.equals(that.prevPrice) : that.prevPrice != null) {
+            return false;
+        }
+        if (prevPriceList != null ? !prevPriceList.equals(that.prevPriceList) : that.prevPriceList != null) {
+            return false;
+        }
+        if (prevProductCategory != null ? !prevProductCategory.equals(that.prevProductCategory) : that.prevProductCategory != null) {
+            return false;
+        }
+        if (prevProductName != null ? !prevProductName.equals(that.prevProductName) : that.prevProductName != null) {
+            return false;
+        }
+        if (prevProductType != null ? !prevProductType.equals(that.prevProductType) : that.prevProductType != null) {
+            return false;
+        }
+        if (prevSlug != null ? !prevSlug.equals(that.prevSlug) : that.prevSlug != null) {
+            return false;
+        }
+        if (prevStartDate != null ? !prevStartDate.equals(that.prevStartDate) : that.prevStartDate != null) {
+            return false;
+        }
+        if (prevState != null ? !prevState.equals(that.prevState) : that.prevState != null) {
+            return false;
+        }
+        if (requestedTimestamp != null ? !requestedTimestamp.equals(that.requestedTimestamp) : that.requestedTimestamp != null) {
+            return false;
+        }
+        if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (bundleId != null ? bundleId.hashCode() : 0);
+        result = 31 * result + (bundleExternalKey != null ? bundleExternalKey.hashCode() : 0);
+        result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
+        result = 31 * result + (requestedTimestamp != null ? requestedTimestamp.hashCode() : 0);
+        result = 31 * result + (event != null ? event.hashCode() : 0);
+        result = 31 * result + (prevProductName != null ? prevProductName.hashCode() : 0);
+        result = 31 * result + (prevProductType != null ? prevProductType.hashCode() : 0);
+        result = 31 * result + (prevProductCategory != null ? prevProductCategory.hashCode() : 0);
+        result = 31 * result + (prevSlug != null ? prevSlug.hashCode() : 0);
+        result = 31 * result + (prevPhase != null ? prevPhase.hashCode() : 0);
+        result = 31 * result + (prevBillingPeriod != null ? prevBillingPeriod.hashCode() : 0);
+        result = 31 * result + (prevPrice != null ? prevPrice.hashCode() : 0);
+        result = 31 * result + (prevPriceList != null ? prevPriceList.hashCode() : 0);
+        result = 31 * result + (prevMrr != null ? prevMrr.hashCode() : 0);
+        result = 31 * result + (prevCurrency != null ? prevCurrency.hashCode() : 0);
+        result = 31 * result + (prevBusinessActive != null ? prevBusinessActive.hashCode() : 0);
+        result = 31 * result + (prevStartDate != null ? prevStartDate.hashCode() : 0);
+        result = 31 * result + (prevState != null ? prevState.hashCode() : 0);
+        result = 31 * result + (nextProductName != null ? nextProductName.hashCode() : 0);
+        result = 31 * result + (nextProductType != null ? nextProductType.hashCode() : 0);
+        result = 31 * result + (nextProductCategory != null ? nextProductCategory.hashCode() : 0);
+        result = 31 * result + (nextSlug != null ? nextSlug.hashCode() : 0);
+        result = 31 * result + (nextPhase != null ? nextPhase.hashCode() : 0);
+        result = 31 * result + (nextBillingPeriod != null ? nextBillingPeriod.hashCode() : 0);
+        result = 31 * result + (nextPrice != null ? nextPrice.hashCode() : 0);
+        result = 31 * result + (nextPriceList != null ? nextPriceList.hashCode() : 0);
+        result = 31 * result + (nextMrr != null ? nextMrr.hashCode() : 0);
+        result = 31 * result + (nextCurrency != null ? nextCurrency.hashCode() : 0);
+        result = 31 * result + (nextBusinessActive != null ? nextBusinessActive.hashCode() : 0);
+        result = 31 * result + (nextStartDate != null ? nextStartDate.hashCode() : 0);
+        result = 31 * result + (nextEndDate != null ? nextEndDate.hashCode() : 0);
+        result = 31 * result + (nextState != null ? nextState.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/user/AnalyticsUserApi.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/user/AnalyticsUserApi.java
new file mode 100644
index 0000000..c4dfdb5
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/user/AnalyticsUserApi.java
@@ -0,0 +1,123 @@
+/*
+ * 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.user;
+
+import java.util.Collection;
+import java.util.UUID;
+
+import org.osgi.service.log.LogService;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsRefreshException;
+import com.ning.billing.osgi.bundles.analytics.BusinessAnalyticsBase;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessAccount;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessField;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessInvoice;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessInvoicePayment;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessOverdueStatus;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessSnapshot;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessSubscriptionTransition;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessTag;
+import com.ning.billing.osgi.bundles.analytics.dao.AnalyticsDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessAccountDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessFieldDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoicePaymentDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessOverdueStatusDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessSubscriptionTransitionDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessTagDao;
+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;
+
+public class AnalyticsUserApi extends BusinessAnalyticsBase {
+
+    private final AnalyticsDao analyticsDao;
+    private final BusinessAccountDao bacDao;
+    private final BusinessSubscriptionTransitionDao bstDao;
+    private final BusinessInvoiceDao binDao;
+    private final BusinessInvoicePaymentDao bipDao;
+    private final BusinessOverdueStatusDao bosDao;
+    private final BusinessFieldDao bFieldDao;
+    private final BusinessTagDao bTagDao;
+
+    public AnalyticsUserApi(final OSGIKillbillLogService logService,
+                            final OSGIKillbillAPI osgiKillbillAPI,
+                            final OSGIKillbillDataSource osgiKillbillDataSource) {
+        super(logService, osgiKillbillAPI);
+        this.analyticsDao = new AnalyticsDao(osgiKillbillAPI, osgiKillbillDataSource);
+        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.bFieldDao = new BusinessFieldDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
+        this.bTagDao = new BusinessTagDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
+    }
+
+    public BusinessSnapshot getBusinessSnapshot(final UUID accountId, final TenantContext context) {
+        // Find account
+        final BusinessAccount businessAccount = analyticsDao.getAccountById(accountId, context);
+
+        // Find all transitions for all bundles for that account, and associated overdue statuses
+        final Collection<BusinessSubscriptionTransition> businessSubscriptionTransitions = analyticsDao.getSubscriptionTransitionsForAccount(accountId, context);
+        final Collection<BusinessOverdueStatus> businessOverdueStatuses = analyticsDao.getOverdueStatusesForAccount(accountId, context);
+
+        // Find all invoices for that account
+        final Collection<BusinessInvoice> businessInvoices = analyticsDao.getInvoicesForAccount(accountId, context);
+
+        // Find all payments for that account
+        final Collection<BusinessInvoicePayment> businessInvoicePayments = analyticsDao.getInvoicePaymentsForAccount(accountId, context);
+
+        // Find all tags for that account
+        final Collection<BusinessTag> businessTags = analyticsDao.getTagsForAccount(accountId, context);
+
+        // Find all fields for that account
+        final Collection<BusinessField> businessFields = analyticsDao.getFieldsForAccount(accountId, context);
+
+        return new BusinessSnapshot(businessAccount,
+                                    businessSubscriptionTransitions,
+                                    businessInvoices,
+                                    businessInvoicePayments,
+                                    businessOverdueStatuses,
+                                    businessTags,
+                                    businessFields);
+    }
+
+    public void rebuildAnalyticsForAccount(final UUID accountId, final CallContext context) throws AnalyticsRefreshException {
+        logService.log(LogService.LOG_INFO, "Starting rebuild of Analytics for account " + accountId);
+
+        // Refresh payments. This will automatically trigger a refresh of invoices and account
+        bipDao.update(accountId, context);
+
+        // Refresh BST
+        bstDao.update(accountId, context);
+
+        // Refresh tags
+        bTagDao.update(accountId, context);
+
+        // Refresh fields
+        bFieldDao.update(accountId, context);
+
+        // Refresh BOS (bundles only for now)
+        bosDao.update(accountId, ObjectType.BUNDLE, context);
+
+        logService.log(LogService.LOG_INFO, "Finished rebuild of Analytics for account " + accountId);
+    }
+}
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..6082aeb
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/BusinessAnalyticsBase.java
@@ -0,0 +1,533 @@
+/*
+ * 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.junction.api.BlockingState;
+import com.ning.billing.junction.api.JunctionApi;
+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.payment.api.Refund;
+import com.ning.billing.util.api.AuditLevel;
+import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldUserApi;
+import com.ning.billing.util.api.RecordIdApi;
+import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.api.TagUserApi;
+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.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillAPI;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
+
+// Wrapper around Kill Bill APIs
+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;
+    }
+
+    //
+    // TENANT
+    //
+
+    protected Long getTenantRecordId(final TenantContext context) throws AnalyticsRefreshException {
+        final RecordIdApi recordIdUserApi = getRecordIdUserApi();
+        return recordIdUserApi.getRecordId(context.getTenantId(), ObjectType.TENANT, context);
+    }
+
+    //
+    // 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);
+    }
+
+    protected Long getAccountRecordId(final UUID accountId, final TenantContext context) throws AnalyticsRefreshException {
+        final RecordIdApi recordIdUserApi = getRecordIdUserApi();
+        return recordIdUserApi.getRecordId(accountId, ObjectType.ACCOUNT, context);
+    }
+
+    //
+    // 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 List<SubscriptionBundle> getSubscriptionBundlesForAccount(final UUID accountId, final TenantContext context) throws AnalyticsRefreshException {
+        final EntitlementUserApi entitlementUserApi = getEntitlementUserApi();
+        return entitlementUserApi.getBundlesForAccount(accountId, context);
+    }
+
+    protected Subscription getSubscription(final UUID subscriptionId, final TenantContext context) throws AnalyticsRefreshException {
+        final EntitlementUserApi entitlementUserApi = getEntitlementUserApi();
+
+        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);
+        }
+    }
+
+    protected AuditLog getSubscriptionEventCreationAuditLog(final UUID subscriptionEventId, final TenantContext context) throws AnalyticsRefreshException {
+        final List<AuditLog> auditLogsForSubscriptionEvent = getAuditUserApi().getAuditLogs(subscriptionEventId, ObjectType.SUBSCRIPTION_EVENT, AuditLevel.MINIMAL, context);
+        for (final AuditLog auditLog : auditLogsForSubscriptionEvent) {
+            if (auditLog.getChangeType().equals(ChangeType.INSERT)) {
+                return auditLog;
+            }
+        }
+
+        throw new AnalyticsRefreshException("Unable to find Subscription event creation audit log for id " + subscriptionEventId);
+    }
+
+    protected Long getSubscriptionEventRecordId(final UUID subscriptionEventId, final TenantContext context) throws AnalyticsRefreshException {
+        final RecordIdApi recordIdUserApi = getRecordIdUserApi();
+        return recordIdUserApi.getRecordId(subscriptionEventId, ObjectType.SUBSCRIPTION_EVENT, context);
+    }
+
+    //
+    // OVERDUE
+    //
+
+    protected List<BlockingState> getBlockingHistory(final UUID overdueableId, final TenantContext context) throws AnalyticsRefreshException {
+        final JunctionApi junctionUserApi = getJunctionUserApi();
+        return junctionUserApi.getBlockingHistory(overdueableId, context);
+    }
+
+    //
+    // BLOCKING STATES
+    //
+
+    protected AuditLog getBlockingStateCreationAuditLog(final UUID blockingStateId, final TenantContext context) throws AnalyticsRefreshException {
+        final List<AuditLog> auditLogsForBlockingState = getAuditUserApi().getAuditLogs(blockingStateId, ObjectType.BLOCKING_STATES, AuditLevel.MINIMAL, context);
+        for (final AuditLog auditLog : auditLogsForBlockingState) {
+            if (auditLog.getChangeType().equals(ChangeType.INSERT)) {
+                return auditLog;
+            }
+        }
+
+        throw new AnalyticsRefreshException("Unable to find Blocking state creation audit log for id " + blockingStateId);
+    }
+
+    protected Long getBlockingStateRecordId(final UUID blockingStateId, final TenantContext context) throws AnalyticsRefreshException {
+        final RecordIdApi recordIdUserApi = getRecordIdUserApi();
+        return recordIdUserApi.getRecordId(blockingStateId, ObjectType.BLOCKING_STATES, context);
+    }
+
+    //
+    // 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 Long getInvoiceRecordId(final UUID invoiceId, final TenantContext context) throws AnalyticsRefreshException {
+        final RecordIdApi recordIdUserApi = getRecordIdUserApi();
+        return recordIdUserApi.getRecordId(invoiceId, ObjectType.INVOICE, context);
+    }
+
+    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 Long getInvoiceItemRecordId(final UUID invoiceItemId, final TenantContext context) throws AnalyticsRefreshException {
+        final RecordIdApi recordIdUserApi = getRecordIdUserApi();
+        return recordIdUserApi.getRecordId(invoiceItemId, ObjectType.INVOICE_ITEM, context);
+    }
+
+    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();
+        return catalogUserApi.getCatalog(null, 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> allInvoicePayments = new LinkedList<InvoicePayment>();
+        for (final Payment payment : payments) {
+            // Retrieve all invoice payment types (including refunds and chargebacks) for that payment
+            allInvoicePayments.addAll(invoicePaymentApi.getInvoicePayments(payment.getId(), context));
+        }
+
+        return allInvoicePayments;
+    }
+
+    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);
+    }
+
+    protected Long getInvoicePaymentRecordId(final UUID invoicePaymentId, final TenantContext context) throws AnalyticsRefreshException {
+        final RecordIdApi recordIdUserApi = getRecordIdUserApi();
+        return recordIdUserApi.getRecordId(invoicePaymentId, ObjectType.INVOICE_PAYMENT, context);
+    }
+
+    //
+    // 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 getPaymentWithPluginInfo(final UUID paymentId, final TenantContext context) throws AnalyticsRefreshException {
+        final PaymentApi paymentApi = getPaymentUserApi();
+
+        try {
+            return paymentApi.getPayment(paymentId, true, context);
+        } catch (PaymentApiException e) {
+            logService.log(LogService.LOG_WARNING, "Error retrieving payment for id " + paymentId, e);
+            throw new AnalyticsRefreshException(e);
+        }
+    }
+
+    protected Refund getRefundWithPluginInfo(final UUID refundId, final TenantContext context) throws AnalyticsRefreshException {
+        final PaymentApi paymentApi = getPaymentUserApi();
+
+        try {
+            return paymentApi.getRefund(refundId, true, context);
+        } catch (PaymentApiException e) {
+            logService.log(LogService.LOG_WARNING, "Error retrieving refund for id " + refundId, 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);
+        }
+    }
+
+    //
+    // FIELD
+    //
+
+    protected Collection<CustomField> getFieldsForAccount(final UUID accountId, final TenantContext context) throws AnalyticsRefreshException {
+        final CustomFieldUserApi tagUserApi = getCustomFieldUserApi();
+        return tagUserApi.getCustomFieldsForAccount(accountId, context);
+    }
+
+    protected AuditLog getFieldCreationAuditLog(final UUID fieldId, final TenantContext context) throws AnalyticsRefreshException {
+        final List<AuditLog> auditLogsForTag = getAuditUserApi().getAuditLogs(fieldId, ObjectType.CUSTOM_FIELD, AuditLevel.MINIMAL, context);
+        for (final AuditLog auditLog : auditLogsForTag) {
+            if (auditLog.getChangeType().equals(ChangeType.INSERT)) {
+                return auditLog;
+            }
+        }
+
+        throw new AnalyticsRefreshException("Unable to find Field creation audit log for id " + fieldId);
+    }
+
+    protected Long getFieldRecordId(final UUID fieldId, final TenantContext context) throws AnalyticsRefreshException {
+        final RecordIdApi recordIdUserApi = getRecordIdUserApi();
+        return recordIdUserApi.getRecordId(fieldId, ObjectType.CUSTOM_FIELD, context);
+    }
+
+    //
+    // TAG
+    //
+
+    protected Collection<Tag> getTagsForAccount(final UUID accountId, final TenantContext context) throws AnalyticsRefreshException {
+        final TagUserApi tagUserApi = getTagUserApi();
+        return tagUserApi.getTagsForAccount(accountId, context);
+    }
+
+    protected TagDefinition getTagDefinition(final UUID tagDefinitionId, final TenantContext context) throws AnalyticsRefreshException {
+        final TagUserApi tagUserApi = getTagUserApi();
+
+        try {
+            return tagUserApi.getTagDefinition(tagDefinitionId, context);
+        } catch (TagDefinitionApiException e) {
+            logService.log(LogService.LOG_WARNING, "Error retrieving tag definition for id " + tagDefinitionId, e);
+            throw new AnalyticsRefreshException(e);
+        }
+    }
+
+    protected AuditLog getTagCreationAuditLog(final UUID tagId, final TenantContext context) throws AnalyticsRefreshException {
+        final List<AuditLog> auditLogsForTag = getAuditUserApi().getAuditLogs(tagId, ObjectType.TAG, AuditLevel.MINIMAL, context);
+        for (final AuditLog auditLog : auditLogsForTag) {
+            if (auditLog.getChangeType().equals(ChangeType.INSERT)) {
+                return auditLog;
+            }
+        }
+
+        throw new AnalyticsRefreshException("Unable to find Tag creation audit log for id " + tagId);
+    }
+
+    protected Long getTagRecordId(final UUID tagId, final TenantContext context) throws AnalyticsRefreshException {
+        final RecordIdApi recordIdUserApi = getRecordIdUserApi();
+        return recordIdUserApi.getRecordId(tagId, ObjectType.TAG, context);
+    }
+
+    //
+    // 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 JunctionApi getJunctionUserApi() throws AnalyticsRefreshException {
+        final JunctionApi junctionApi = osgiKillbillAPI.getJunctionApi();
+        if (junctionApi == null) {
+            throw new AnalyticsRefreshException("Error retrieving junctionApi");
+        }
+        return junctionApi;
+    }
+
+    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;
+    }
+
+    private CustomFieldUserApi getCustomFieldUserApi() throws AnalyticsRefreshException {
+        final CustomFieldUserApi fieldUserApi = osgiKillbillAPI.getCustomFieldUserApi();
+        if (fieldUserApi == null) {
+            throw new AnalyticsRefreshException("Error retrieving fieldUserApi");
+        }
+        return fieldUserApi;
+    }
+
+    private TagUserApi getTagUserApi() throws AnalyticsRefreshException {
+        final TagUserApi tagUserApi = osgiKillbillAPI.getTagUserApi();
+        if (tagUserApi == null) {
+            throw new AnalyticsRefreshException("Error retrieving tagUserApi");
+        }
+        return tagUserApi;
+    }
+
+    private RecordIdApi getRecordIdUserApi() throws AnalyticsRefreshException {
+        final RecordIdApi recordIdApi = osgiKillbillAPI.getRecordIdApi();
+        if (recordIdApi == null) {
+            throw new AnalyticsRefreshException("Error retrieving recordIdApi");
+        }
+        return recordIdApi;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/AnalyticsDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/AnalyticsDao.java
index 0541d46..ac39ac7 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/AnalyticsDao.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/AnalyticsDao.java
@@ -16,37 +16,167 @@
 
 package com.ning.billing.osgi.bundles.analytics.dao;
 
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.UUID;
 
-import com.ning.billing.analytics.api.TimeSeriesData;
-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;
-import com.ning.billing.osgi.bundles.analytics.model.BusinessInvoiceModelDao;
-import com.ning.billing.osgi.bundles.analytics.model.BusinessInvoicePaymentModelDao;
-import com.ning.billing.osgi.bundles.analytics.model.BusinessOverdueStatusModelDao;
-import com.ning.billing.osgi.bundles.analytics.model.BusinessSubscriptionTransitionModelDao;
-import com.ning.billing.util.callcontext.InternalTenantContext;
+import org.skife.jdbi.v2.DBI;
 
-public interface AnalyticsDao {
+import com.ning.billing.ObjectType;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessAccount;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessField;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessInvoice;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessInvoicePayment;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessOverdueStatus;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessSubscriptionTransition;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessTag;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessFieldModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceItemBaseModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentBaseModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessOverdueStatusModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessSubscriptionTransitionModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessTagModelDao;
+import com.ning.billing.util.api.RecordIdApi;
+import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillAPI;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillDataSource;
 
-    TimeSeriesData getAccountsCreatedOverTime(InternalTenantContext context);
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 
-    TimeSeriesData getSubscriptionsCreatedOverTime(String productType, String slug, InternalTenantContext context);
+public class AnalyticsDao {
 
-    BusinessAccountModelDao getAccountByKey(String accountKey, InternalTenantContext context);
+    private final OSGIKillbillAPI osgiKillbillAPI;
+    private final BusinessAnalyticsSqlDao sqlDao;
 
-    List<BusinessSubscriptionTransitionModelDao> getTransitionsByKey(String externalKey, InternalTenantContext context);
+    public AnalyticsDao(final OSGIKillbillAPI osgiKillbillAPI,
+                        final OSGIKillbillDataSource osgiKillbillDataSource) {
+        this.osgiKillbillAPI = osgiKillbillAPI;
+        final DBI dbi = BusinessDBIProvider.get(osgiKillbillDataSource.getDataSource());
+        sqlDao = dbi.onDemand(BusinessAnalyticsSqlDao.class);
+    }
 
-    List<BusinessSubscriptionTransitionModelDao> getTransitionsForAccount(String accountKey, InternalTenantContext context);
+    public BusinessAccount getAccountById(final UUID accountId, final TenantContext context) {
+        final Long accountRecordId = getAccountRecordId(accountId, context);
+        final Long tenantRecordId = getTenantRecordId(context);
 
-    List<BusinessInvoiceModelDao> getInvoicesByKey(String accountKey, InternalTenantContext context);
+        final BusinessAccountModelDao businessAccountModelDao = sqlDao.getAccountByAccountRecordId(accountRecordId, tenantRecordId, context);
+        if (businessAccountModelDao == null) {
+            return null;
+        } else {
+            return new BusinessAccount(businessAccountModelDao);
+        }
+    }
 
-    List<BusinessInvoiceItemModelDao> getInvoiceItemsForInvoice(String invoiceId, InternalTenantContext context);
+    public Collection<BusinessSubscriptionTransition> getSubscriptionTransitionsForAccount(final UUID accountId, final TenantContext context) {
+        final Long accountRecordId = getAccountRecordId(accountId, context);
+        final Long tenantRecordId = getTenantRecordId(context);
 
-    List<BusinessInvoicePaymentModelDao> getInvoicePaymentsForAccountByKey(String accountKey, InternalTenantContext context);
+        final List<BusinessSubscriptionTransitionModelDao> businessSubscriptionTransitionModelDaos = sqlDao.getSubscriptionTransitionsByAccountRecordId(accountRecordId, tenantRecordId, context);
+        return Lists.transform(businessSubscriptionTransitionModelDaos, new Function<BusinessSubscriptionTransitionModelDao, BusinessSubscriptionTransition>() {
+            @Override
+            public BusinessSubscriptionTransition apply(final BusinessSubscriptionTransitionModelDao input) {
+                return new BusinessSubscriptionTransition(input);
+            }
+        });
+    }
 
-    List<BusinessOverdueStatusModelDao> getOverdueStatusesForBundleByKey(String externalKey, InternalTenantContext context);
+    public Collection<BusinessOverdueStatus> getOverdueStatusesForAccount(final UUID accountId, final TenantContext context) {
+        final Long accountRecordId = getAccountRecordId(accountId, context);
+        final Long tenantRecordId = getTenantRecordId(context);
 
-    List<BusinessAccountTagModelDao> getTagsForAccount(String accountKey, InternalTenantContext context);
+        final List<BusinessOverdueStatusModelDao> businessOverdueStatusModelDaos = sqlDao.getOverdueStatusesByAccountRecordId(accountRecordId, tenantRecordId, context);
+        return Lists.transform(businessOverdueStatusModelDaos, new Function<BusinessOverdueStatusModelDao, BusinessOverdueStatus>() {
+            @Override
+            public BusinessOverdueStatus apply(final BusinessOverdueStatusModelDao input) {
+                return new BusinessOverdueStatus(input);
+            }
+        });
+    }
+
+    public Collection<BusinessInvoice> getInvoicesForAccount(final UUID accountId, final TenantContext context) {
+        final Long accountRecordId = getAccountRecordId(accountId, context);
+        final Long tenantRecordId = getTenantRecordId(context);
+
+        final List<BusinessInvoiceItemBaseModelDao> businessInvoiceItemModelDaos = sqlDao.getInvoiceItemsByAccountRecordId(accountRecordId, tenantRecordId, context);
+        final Map<UUID, List<BusinessInvoiceItemBaseModelDao>> itemsPerInvoice = new LinkedHashMap<UUID, List<BusinessInvoiceItemBaseModelDao>>();
+        for (final BusinessInvoiceItemBaseModelDao businessInvoiceModelDao : businessInvoiceItemModelDaos) {
+            if (itemsPerInvoice.get(businessInvoiceModelDao.getInvoiceId()) == null) {
+                itemsPerInvoice.put(businessInvoiceModelDao.getInvoiceId(), new LinkedList<BusinessInvoiceItemBaseModelDao>());
+            }
+            itemsPerInvoice.get(businessInvoiceModelDao.getInvoiceId()).add(businessInvoiceModelDao);
+        }
+
+        final List<BusinessInvoiceModelDao> businessInvoiceModelDaos = sqlDao.getInvoicesByAccountRecordId(accountRecordId, tenantRecordId, context);
+        return Lists.transform(businessInvoiceModelDaos, new Function<BusinessInvoiceModelDao, BusinessInvoice>() {
+            @Override
+            public BusinessInvoice apply(final BusinessInvoiceModelDao input) {
+                return new BusinessInvoice(input, Objects.firstNonNull(itemsPerInvoice.get(input.getInvoiceId()), ImmutableList.<BusinessInvoiceItemBaseModelDao>of()));
+            }
+        });
+    }
+
+    public Collection<BusinessInvoicePayment> getInvoicePaymentsForAccount(final UUID accountId, final TenantContext context) {
+        final Long accountRecordId = getAccountRecordId(accountId, context);
+        final Long tenantRecordId = getTenantRecordId(context);
+
+        final List<BusinessInvoicePaymentBaseModelDao> businessInvoicePaymentBaseModelDaos = sqlDao.getInvoicePaymentsByAccountRecordId(accountRecordId, tenantRecordId, context);
+        return Lists.transform(businessInvoicePaymentBaseModelDaos, new Function<BusinessInvoicePaymentBaseModelDao, BusinessInvoicePayment>() {
+            @Override
+            public BusinessInvoicePayment apply(final BusinessInvoicePaymentBaseModelDao input) {
+                return new BusinessInvoicePayment(input);
+            }
+        });
+    }
+
+    public Collection<BusinessField> getFieldsForAccount(final UUID accountId, final TenantContext context) {
+        final Long accountRecordId = getAccountRecordId(accountId, context);
+        final Long tenantRecordId = getTenantRecordId(context);
+
+        final List<BusinessFieldModelDao> businessFieldModelDaos = new LinkedList<BusinessFieldModelDao>();
+        businessFieldModelDaos.addAll(sqlDao.getAccountFieldsByAccountRecordId(accountRecordId, tenantRecordId, context));
+        businessFieldModelDaos.addAll(sqlDao.getInvoiceFieldsByAccountRecordId(accountRecordId, tenantRecordId, context));
+        businessFieldModelDaos.addAll(sqlDao.getInvoicePaymentFieldsByAccountRecordId(accountRecordId, tenantRecordId, context));
+
+        return Lists.transform(businessFieldModelDaos, new Function<BusinessFieldModelDao, BusinessField>() {
+            @Override
+            public BusinessField apply(final BusinessFieldModelDao input) {
+                return BusinessField.create(input);
+            }
+        });
+    }
+
+    public Collection<BusinessTag> getTagsForAccount(final UUID accountId, final TenantContext context) {
+        final Long accountRecordId = getAccountRecordId(accountId, context);
+        final Long tenantRecordId = getTenantRecordId(context);
+
+        final List<BusinessTagModelDao> businessTagModelDaos = new LinkedList<BusinessTagModelDao>();
+        businessTagModelDaos.addAll(sqlDao.getAccountTagsByAccountRecordId(accountRecordId, tenantRecordId, context));
+        businessTagModelDaos.addAll(sqlDao.getInvoiceTagsByAccountRecordId(accountRecordId, tenantRecordId, context));
+        businessTagModelDaos.addAll(sqlDao.getInvoicePaymentTagsByAccountRecordId(accountRecordId, tenantRecordId, context));
+
+        return Lists.transform(businessTagModelDaos, new Function<BusinessTagModelDao, BusinessTag>() {
+            @Override
+            public BusinessTag apply(final BusinessTagModelDao input) {
+                return BusinessTag.create(input);
+            }
+        });
+    }
+
+    private Long getAccountRecordId(final UUID accountId, final TenantContext context) {
+        final RecordIdApi recordIdApi = osgiKillbillAPI.getRecordIdApi();
+        return recordIdApi == null ? -1L : recordIdApi.getRecordId(accountId, ObjectType.ACCOUNT, context);
+    }
+
+    private Long getTenantRecordId(final TenantContext context) {
+        final RecordIdApi recordIdApi = osgiKillbillAPI.getRecordIdApi();
+        return recordIdApi == null ? -1L : recordIdApi.getRecordId(context.getTenantId(), ObjectType.TENANT, context);
+    }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessAccountDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessAccountDao.java
new file mode 100644
index 0000000..d31593d
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessAccountDao.java
@@ -0,0 +1,104 @@
+/*
+ * 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.dao;
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.UUID;
+
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsRefreshException;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountModelDao;
+import com.ning.billing.payment.api.Payment;
+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 update(final UUID accountId, final CallContext context) throws AnalyticsRefreshException {
+        final Account account = getAccount(accountId, context);
+
+        // Recompute the account record
+        final BusinessAccountModelDao bac = createBusinessAccount(account, context);
+
+        sqlDao.inTransaction(new Transaction<Void, BusinessAnalyticsSqlDao>() {
+            @Override
+            public Void inTransaction(final BusinessAnalyticsSqlDao transactional, final TransactionStatus status) throws Exception {
+                updateInTransaction(bac, transactional, context);
+                return null;
+            }
+        });
+    }
+
+    // Note: computing the BusinessAccountModelDao object is fairly expensive, hence should be done outside of the transaction
+    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 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 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;
+            }
+        }
+
+        final Long accountRecordId = getAccountRecordId(account.getId(), context);
+        final Long tenantRecordId = getTenantRecordId(context);
+
+        return new BusinessAccountModelDao(account,
+                                           accountRecordId,
+                                           accountBalance,
+                                           lastInvoice,
+                                           lastPayment,
+                                           creationAuditLog,
+                                           tenantRecordId);
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessAnalyticsSqlDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessAnalyticsSqlDao.java
new file mode 100644
index 0000000..5ba5502
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessAnalyticsSqlDao.java
@@ -0,0 +1,118 @@
+/*
+ * 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.dao;
+
+import java.util.List;
+
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.Define;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+
+import com.ning.billing.commons.jdbi.binder.SmartBindBean;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountFieldModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountTagModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceFieldModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceItemBaseModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentBaseModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentFieldModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentTagModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceTagModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessModelDaoBase;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessOverdueStatusModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessSubscriptionTransitionModelDao;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
+
+public interface BusinessAnalyticsSqlDao extends Transactional<BusinessAnalyticsSqlDao> {
+
+    // Note: the CallContext and TenantContext are not bound for now since they are not used (and createdDate would conflict)
+
+    @SqlUpdate
+    public void create(final String tableName,
+                       @SmartBindBean final BusinessModelDaoBase entity,
+                       final CallContext callContext);
+
+    @SqlUpdate
+    public void deleteByAccountRecordId(@Define("tableName") final String tableName,
+                                        @Bind("accountRecordId") final Long accountRecordId,
+                                        @Bind("tenantRecordId") final Long tenantRecordId,
+                                        final CallContext callContext);
+
+    @SqlQuery
+    public BusinessAccountModelDao getAccountByAccountRecordId(@Bind("accountRecordId") final Long accountRecordId,
+                                                               @Bind("tenantRecordId") final Long tenantRecordId,
+                                                               final TenantContext tenantContext);
+
+    @SqlQuery
+    public List<BusinessSubscriptionTransitionModelDao> getSubscriptionTransitionsByAccountRecordId(@Bind("accountRecordId") final Long accountRecordId,
+                                                                                                    @Bind("tenantRecordId") final Long tenantRecordId,
+                                                                                                    final TenantContext tenantContext);
+
+    @SqlQuery
+    public List<BusinessOverdueStatusModelDao> getOverdueStatusesByAccountRecordId(@Bind("accountRecordId") final Long accountRecordId,
+                                                                                   @Bind("tenantRecordId") final Long tenantRecordId,
+                                                                                   final TenantContext tenantContext);
+
+    @SqlQuery
+    public List<BusinessInvoiceModelDao> getInvoicesByAccountRecordId(@Bind("accountRecordId") final Long accountRecordId,
+                                                                      @Bind("tenantRecordId") final Long tenantRecordId,
+                                                                      final TenantContext tenantContext);
+
+    @SqlQuery
+    public List<BusinessInvoiceItemBaseModelDao> getInvoiceItemsByAccountRecordId(@Bind("accountRecordId") final Long accountRecordId,
+                                                                                  @Bind("tenantRecordId") final Long tenantRecordId,
+                                                                                  final TenantContext tenantContext);
+
+    @SqlQuery
+    public List<BusinessInvoicePaymentBaseModelDao> getInvoicePaymentsByAccountRecordId(@Bind("accountRecordId") final Long accountRecordId,
+                                                                                        @Bind("tenantRecordId") final Long tenantRecordId,
+                                                                                        final TenantContext tenantContext);
+
+    @SqlQuery
+    public List<BusinessAccountFieldModelDao> getAccountFieldsByAccountRecordId(@Bind("accountRecordId") final Long accountRecordId,
+                                                                                @Bind("tenantRecordId") final Long tenantRecordId,
+                                                                                final TenantContext tenantContext);
+
+    @SqlQuery
+    public List<BusinessInvoiceFieldModelDao> getInvoiceFieldsByAccountRecordId(@Bind("accountRecordId") final Long accountRecordId,
+                                                                                @Bind("tenantRecordId") final Long tenantRecordId,
+                                                                                final TenantContext tenantContext);
+
+    @SqlQuery
+    public List<BusinessInvoicePaymentFieldModelDao> getInvoicePaymentFieldsByAccountRecordId(@Bind("accountRecordId") final Long accountRecordId,
+                                                                                              @Bind("tenantRecordId") final Long tenantRecordId,
+                                                                                              final TenantContext tenantContext);
+
+    @SqlQuery
+    public List<BusinessAccountTagModelDao> getAccountTagsByAccountRecordId(@Bind("accountRecordId") final Long accountRecordId,
+                                                                            @Bind("tenantRecordId") final Long tenantRecordId,
+                                                                            final TenantContext tenantContext);
+
+    @SqlQuery
+    public List<BusinessInvoiceTagModelDao> getInvoiceTagsByAccountRecordId(@Bind("accountRecordId") final Long accountRecordId,
+                                                                            @Bind("tenantRecordId") final Long tenantRecordId,
+                                                                            final TenantContext tenantContext);
+
+    @SqlQuery
+    public List<BusinessInvoicePaymentTagModelDao> getInvoicePaymentTagsByAccountRecordId(@Bind("accountRecordId") final Long accountRecordId,
+                                                                                          @Bind("tenantRecordId") final Long tenantRecordId,
+                                                                                          final TenantContext tenantContext);
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessDBIProvider.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessDBIProvider.java
new file mode 100644
index 0000000..f6718ac
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessDBIProvider.java
@@ -0,0 +1,100 @@
+/*
+ * 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.dao;
+
+import javax.sql.DataSource;
+
+import org.skife.jdbi.v2.Binding;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.Argument;
+
+import com.ning.billing.commons.jdbi.ReusableStringTemplate3StatementLocator;
+import com.ning.billing.commons.jdbi.mapper.LowerToCamelBeanMapperFactory;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountFieldModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountTagModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceAdjustmentModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceFieldModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceItemAdjustmentModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceItemCreditModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceItemModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentChargebackModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentFieldModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentRefundModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentTagModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceTagModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessOverdueStatusModelDao;
+
+import com.google.common.base.CaseFormat;
+
+public class BusinessDBIProvider {
+
+    private BusinessDBIProvider() {}
+
+    public static DBI get(final DataSource dataSource) {
+        final DBI dbi = new DBI(dataSource);
+
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessAccountFieldModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessAccountModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessAccountTagModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessInvoiceAdjustmentModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessInvoiceFieldModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessInvoiceItemAdjustmentModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessInvoiceItemCreditModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessInvoiceItemModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessInvoiceModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessInvoicePaymentChargebackModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessInvoicePaymentFieldModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessInvoicePaymentDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessInvoicePaymentRefundModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessInvoicePaymentTagModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessInvoiceTagModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessOverdueStatusModelDao.class));
+        dbi.registerMapper(new LowerToCamelBeanMapperFactory(BusinessSubscriptionTransitionDao.class));
+
+        dbi.setStatementLocator(new AnalyticsStatementLocator());
+
+        return dbi;
+    }
+
+    private static final class AnalyticsStatementLocator extends ReusableStringTemplate3StatementLocator {
+
+        public AnalyticsStatementLocator() {
+            super(BusinessAnalyticsSqlDao.class, true, true);
+        }
+
+        @Override
+        public String locate(final String name, final StatementContext ctx) throws Exception {
+            // Rewrite create to createBac, createBin, createBiia, etc.
+            if ("create".equals(name)) {
+                final Binding binding = ctx.getBinding();
+                if (binding != null) {
+                    final Argument tableNameArgument = binding.forName("tableName");
+                    if (tableNameArgument != null) {
+                        // Lame, rely on toString
+                        final String tableName = tableNameArgument.toString();
+                        final String newQueryName = name + CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, tableName);
+                        return super.locate(newQueryName, ctx);
+                    }
+                }
+            }
+            return super.locate(name, ctx);
+        }
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessFieldDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessFieldDao.java
new file mode 100644
index 0000000..5f27d3e
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessFieldDao.java
@@ -0,0 +1,93 @@
+/*
+ * 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.dao;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.UUID;
+
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsRefreshException;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessFieldModelDao;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.customfield.CustomField;
+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 BusinessFieldDao extends BusinessAnalyticsDaoBase {
+
+    public BusinessFieldDao(final OSGIKillbillLogService logService,
+                            final OSGIKillbillAPI osgiKillbillAPI,
+                            final OSGIKillbillDataSource osgiKillbillDataSource) {
+        super(logService, osgiKillbillAPI, osgiKillbillDataSource);
+    }
+
+    public void update(final UUID accountId, final CallContext context) throws AnalyticsRefreshException {
+        final Account account = getAccount(accountId, context);
+
+        final Collection<BusinessFieldModelDao> fieldModelDaos = createBusinessFields(account, context);
+
+        sqlDao.inTransaction(new Transaction<Void, BusinessAnalyticsSqlDao>() {
+            @Override
+            public Void inTransaction(final BusinessAnalyticsSqlDao transactional, final TransactionStatus status) throws Exception {
+                updateInTransaction(fieldModelDaos, transactional, context);
+                return null;
+            }
+        });
+    }
+
+    private void updateInTransaction(final Collection<BusinessFieldModelDao> fieldModelDaos, final BusinessAnalyticsSqlDao transactional, final CallContext context) {
+        if (fieldModelDaos.size() == 0) {
+            return;
+        }
+
+        // We assume all fieldModelDaos are for a single type
+        final BusinessFieldModelDao firstFieldModelDao = fieldModelDaos.iterator().next();
+        transactional.deleteByAccountRecordId(firstFieldModelDao.getTableName(), firstFieldModelDao.getAccountRecordId(), firstFieldModelDao.getTenantRecordId(), context);
+
+        for (final BusinessFieldModelDao fieldModelDao : fieldModelDaos) {
+            transactional.create(fieldModelDao.getTableName(), fieldModelDao, context);
+        }
+    }
+
+    private Collection<BusinessFieldModelDao> createBusinessFields(final Account account, final CallContext context) throws AnalyticsRefreshException {
+        final Long accountRecordId = getAccountRecordId(account.getId(), context);
+        final Long tenantRecordId = getTenantRecordId(context);
+
+        final Collection<CustomField> fields = getFieldsForAccount(account.getId(), context);
+
+        final Collection<BusinessFieldModelDao> fieldModelDaos = new LinkedList<BusinessFieldModelDao>();
+        for (final CustomField field : fields) {
+            final Long customFieldRecordId = getFieldRecordId(field.getId(), context);
+            final AuditLog creationAuditLog = getFieldCreationAuditLog(field.getId(), context);
+            final BusinessFieldModelDao fieldModelDao = BusinessFieldModelDao.create(account,
+                                                                                     accountRecordId,
+                                                                                     field,
+                                                                                     customFieldRecordId,
+                                                                                     creationAuditLog,
+                                                                                     tenantRecordId);
+            fieldModelDaos.add(fieldModelDao);
+        }
+
+        return fieldModelDaos;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceDao.java
new file mode 100644
index 0000000..0f31692
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoiceDao.java
@@ -0,0 +1,203 @@
+/*
+ * 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.dao;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.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.AnalyticsRefreshException;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceItemBaseModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceModelDao;
+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;
+
+public class BusinessInvoiceDao extends BusinessAnalyticsDaoBase {
+
+    private final BusinessAccountDao businessAccountDao;
+
+    public BusinessInvoiceDao(final OSGIKillbillLogService logService,
+                              final OSGIKillbillAPI osgiKillbillAPI,
+                              final OSGIKillbillDataSource osgiKillbillDataSource,
+                              final BusinessAccountDao businessAccountDao) {
+        super(logService, osgiKillbillAPI, osgiKillbillDataSource);
+        this.businessAccountDao = businessAccountDao;
+    }
+
+    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 {
+        final Long accountRecordId = getAccountRecordId(account.getId(), context);
+        final Long tenantRecordId = getTenantRecordId(context);
+
+        // Lookup the invoices for that account
+        final Map<BusinessInvoiceModelDao, Collection<BusinessInvoiceItemBaseModelDao>> businessInvoices = new HashMap<BusinessInvoiceModelDao, Collection<BusinessInvoiceItemBaseModelDao>>();
+
+        // Create the business invoice and associated business invoice items
+        final Collection<Invoice> invoices = getInvoicesByAccountId(account.getId(), context);
+        for (final Invoice invoice : invoices) {
+            final Long invoiceRecordId = getInvoiceRecordId(invoice.getId(), context);
+            final AuditLog creationAuditLog = getInvoiceCreationAuditLog(invoice.getId(), context);
+            final BusinessInvoiceModelDao businessInvoice = new BusinessInvoiceModelDao(account,
+                                                                                        accountRecordId,
+                                                                                        invoice,
+                                                                                        invoiceRecordId,
+                                                                                        creationAuditLog,
+                                                                                        tenantRecordId);
+
+            final List<BusinessInvoiceItemBaseModelDao> businessInvoiceItems = new ArrayList<BusinessInvoiceItemBaseModelDao>();
+            for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) {
+                final BusinessInvoiceItemBaseModelDao businessInvoiceItem = createBusinessInvoiceItem(account,
+                                                                                                      invoice,
+                                                                                                      invoiceItem,
+                                                                                                      // TODO Will be used for REPAIR_ADJ
+                                                                                                      null,
+                                                                                                      context);
+                if (businessInvoiceItem != null) {
+                    businessInvoiceItems.add(businessInvoiceItem);
+                }
+            }
+
+            businessInvoices.put(businessInvoice, businessInvoiceItems);
+        }
+
+        return businessInvoices;
+    }
+
+    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);
+        }
+    }
+
+    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 BusinessAnalyticsSqlDao transactional,
+                                            final BusinessInvoiceModelDao invoice,
+                                            final Iterable<BusinessInvoiceItemBaseModelDao> invoiceItems,
+                                            final CallContext context) {
+        // Create the invoice
+        transactional.create(invoice.getTableName(), invoice, context);
+
+        // Add associated invoice items
+        for (final BusinessInvoiceItemBaseModelDao invoiceItem : invoiceItems) {
+            transactional.create(invoiceItem.getTableName(), invoiceItem, context);
+        }
+    }
+
+    private BusinessInvoiceItemBaseModelDao createBusinessInvoiceItem(final Account account,
+                                                                      final Invoice invoice,
+                                                                      final InvoiceItem invoiceItem,
+                                                                      @Nullable final Long secondInvoiceItemRecordId,
+                                                                      final TenantContext context) throws AnalyticsRefreshException {
+        SubscriptionBundle bundle = null;
+        // Subscription and bundle could be null for e.g. credits or adjustments
+        if (invoiceItem.getBundleId() != null) {
+            bundle = getSubscriptionBundle(invoiceItem.getBundleId(), context);
+        }
+
+        Plan plan = null;
+        if (invoiceItem.getPlanName() != null) {
+            plan = getPlanFromInvoiceItem(invoiceItem, context);
+        }
+
+        PlanPhase planPhase = null;
+        if (invoiceItem.getSubscriptionId() != null && invoiceItem.getPhaseName() != null) {
+            planPhase = getPlanPhaseFromInvoiceItem(invoiceItem, context);
+        }
+
+        final Long invoiceItemRecordId = getInvoiceItemRecordId(invoiceItem.getId(), context);
+        final AuditLog creationAuditLog = getInvoiceItemCreationAuditLog(invoiceItem.getId(), context);
+        final Long accountRecordId = getAccountRecordId(account.getId(), context);
+        final Long tenantRecordId = getTenantRecordId(context);
+
+        return BusinessInvoiceItemBaseModelDao.create(account,
+                                                      accountRecordId,
+                                                      invoice,
+                                                      invoiceItem,
+                                                      invoiceItemRecordId,
+                                                      secondInvoiceItemRecordId,
+                                                      bundle,
+                                                      plan,
+                                                      planPhase,
+                                                      creationAuditLog,
+                                                      tenantRecordId);
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoicePaymentDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoicePaymentDao.java
new file mode 100644
index 0000000..65f5108
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessInvoicePaymentDao.java
@@ -0,0 +1,143 @@
+/*
+ * 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.dao;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.UUID;
+
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+
+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.osgi.bundles.analytics.AnalyticsRefreshException;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceItemBaseModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentBaseModelDao;
+import com.ning.billing.payment.api.Payment;
+import com.ning.billing.payment.api.PaymentMethod;
+import com.ning.billing.payment.api.Refund;
+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 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 invoice payments
+        final Collection<BusinessInvoicePaymentBaseModelDao> businessInvoicePayments = createBusinessInvoicePayments(account, 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;
+            }
+        });
+    }
+
+    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);
+
+        // 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 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);
+        }
+
+        for (final BusinessInvoicePaymentBaseModelDao invoicePayment : businessInvoicePayments) {
+            transactional.create(invoicePayment.getTableName(), invoicePayment, context);
+        }
+    }
+
+    private Collection<BusinessInvoicePaymentBaseModelDao> createBusinessInvoicePayments(final Account account,
+                                                                                         final CallContext context) throws AnalyticsRefreshException {
+        final Collection<BusinessInvoicePaymentBaseModelDao> businessInvoicePayments = new LinkedList<BusinessInvoicePaymentBaseModelDao>();
+
+        final Long accountRecordId = getAccountRecordId(account.getId(), context);
+        final Long tenantRecordId = getTenantRecordId(context);
+
+        final Collection<InvoicePayment> invoicePayments = getAccountInvoicePayments(account.getId(), context);
+        for (final InvoicePayment invoicePayment : invoicePayments) {
+            final Long invoicePaymentRecordId = getInvoicePaymentRecordId(invoicePayment.getId(), context);
+
+            final Invoice invoice = getInvoice(invoicePayment.getInvoiceId(), context);
+            final Payment payment = getPaymentWithPluginInfo(invoicePayment.getPaymentId(), context);
+            Refund refund = null;
+            if (invoicePayment.getPaymentCookieId() != null) {
+                refund = getRefundWithPluginInfo(invoicePayment.getPaymentCookieId(), context);
+            }
+
+            final PaymentMethod paymentMethod = getPaymentMethod(payment.getPaymentMethodId(), context);
+            final AuditLog creationAuditLog = getInvoicePaymentCreationAuditLog(invoicePayment.getId(), context);
+
+            final BusinessInvoicePaymentBaseModelDao businessInvoicePayment = BusinessInvoicePaymentBaseModelDao.create(account,
+                                                                                                                        accountRecordId,
+                                                                                                                        invoice,
+                                                                                                                        invoicePayment,
+                                                                                                                        invoicePaymentRecordId,
+                                                                                                                        payment,
+                                                                                                                        refund,
+                                                                                                                        paymentMethod,
+                                                                                                                        creationAuditLog,
+                                                                                                                        tenantRecordId);
+            if (businessInvoicePayment != null) {
+                businessInvoicePayments.add(businessInvoicePayment);
+            }
+        }
+
+        return businessInvoicePayments;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessOverdueStatusDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessOverdueStatusDao.java
new file mode 100644
index 0000000..7374db4
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessOverdueStatusDao.java
@@ -0,0 +1,124 @@
+/*
+ * 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.dao;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.osgi.service.log.LogService;
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.junction.api.BlockingState;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsRefreshException;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessOverdueStatusModelDao;
+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;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+public class BusinessOverdueStatusDao extends BusinessAnalyticsDaoBase {
+
+    public BusinessOverdueStatusDao(final OSGIKillbillLogService logService,
+                                    final OSGIKillbillAPI osgiKillbillAPI,
+                                    final OSGIKillbillDataSource osgiKillbillDataSource) {
+        super(logService, osgiKillbillAPI, osgiKillbillDataSource);
+    }
+
+    public void update(final UUID accountId, final ObjectType objectType, final CallContext context) throws AnalyticsRefreshException {
+        if (ObjectType.BUNDLE.equals(objectType)) {
+            updateForBundle(accountId, context);
+        } else {
+            logService.log(LogService.LOG_WARNING, String.format("Ignoring overdue status change for account id %s (type %s)", accountId, objectType.toString()));
+        }
+    }
+
+    private void updateForBundle(final UUID accountId, final CallContext context) throws AnalyticsRefreshException {
+        final Account account = getAccount(accountId, context);
+
+        final Collection<SubscriptionBundle> bundles = getSubscriptionBundlesForAccount(accountId, context);
+        final Collection<BusinessOverdueStatusModelDao> businessOverdueStatuses = new LinkedList<BusinessOverdueStatusModelDao>();
+        for (final SubscriptionBundle bundle : bundles) {
+            // Recompute all blocking states for that bundle
+            businessOverdueStatuses.addAll(createBusinessOverdueStatuses(account, bundle, context));
+        }
+
+        sqlDao.inTransaction(new Transaction<Void, BusinessAnalyticsSqlDao>() {
+            @Override
+            public Void inTransaction(final BusinessAnalyticsSqlDao transactional, final TransactionStatus status) throws Exception {
+                updateInTransaction(businessOverdueStatuses, transactional, context);
+                return null;
+            }
+        });
+    }
+
+    private void updateInTransaction(final Collection<BusinessOverdueStatusModelDao> businessOverdueStatuses, final BusinessAnalyticsSqlDao transactional, final CallContext context) {
+        if (businessOverdueStatuses.size() == 0) {
+            return;
+        }
+
+        final BusinessOverdueStatusModelDao firstBst = businessOverdueStatuses.iterator().next();
+        transactional.deleteByAccountRecordId(firstBst.getTableName(), firstBst.getAccountRecordId(), firstBst.getTenantRecordId(), context);
+
+        for (final BusinessOverdueStatusModelDao bst : businessOverdueStatuses) {
+            transactional.create(bst.getTableName(), bst, context);
+        }
+    }
+
+    private Collection<BusinessOverdueStatusModelDao> createBusinessOverdueStatuses(final Account account,
+                                                                                    final SubscriptionBundle subscriptionBundle,
+                                                                                    final CallContext context) throws AnalyticsRefreshException {
+        final Collection<BusinessOverdueStatusModelDao> businessOverdueStatuses = new LinkedList<BusinessOverdueStatusModelDao>();
+
+        final List<BlockingState> blockingStatesOrdered = getBlockingHistory(subscriptionBundle.getId(), context);
+        if (blockingStatesOrdered.size() == 0) {
+            return businessOverdueStatuses;
+        }
+
+        final Long accountRecordId = getAccountRecordId(account.getId(), context);
+        final Long tenantRecordId = getTenantRecordId(context);
+
+        final List<BlockingState> blockingStates = Lists.reverse(ImmutableList.<BlockingState>copyOf(blockingStatesOrdered));
+        DateTime previousStartDate = null;
+        for (final BlockingState state : blockingStates) {
+            final Long blockingStateRecordId = getBlockingStateRecordId(state.getId(), context);
+            final AuditLog creationAuditLog = getBlockingStateCreationAuditLog(state.getId(), context);
+            final BusinessOverdueStatusModelDao overdueStatus = new BusinessOverdueStatusModelDao(account,
+                                                                                                  accountRecordId,
+                                                                                                  subscriptionBundle,
+                                                                                                  state,
+                                                                                                  blockingStateRecordId,
+                                                                                                  previousStartDate,
+                                                                                                  creationAuditLog,
+                                                                                                  tenantRecordId);
+            businessOverdueStatuses.add(overdueStatus);
+            previousStartDate = state.getTimestamp();
+        }
+
+        return businessOverdueStatuses;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessSubscriptionTransitionDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessSubscriptionTransitionDao.java
new file mode 100644
index 0000000..44d6b3c
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessSubscriptionTransitionDao.java
@@ -0,0 +1,148 @@
+/*
+ * 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.dao;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsRefreshException;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessSubscription;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessSubscriptionEvent;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessSubscriptionTransitionModelDao;
+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 BusinessSubscriptionTransitionDao extends BusinessAnalyticsDaoBase {
+
+    public BusinessSubscriptionTransitionDao(final OSGIKillbillLogService logService,
+                                             final OSGIKillbillAPI osgiKillbillAPI,
+                                             final OSGIKillbillDataSource osgiKillbillDataSource) {
+        super(logService, osgiKillbillAPI, osgiKillbillDataSource);
+    }
+
+    public void update(final UUID accountId, final CallContext context) throws AnalyticsRefreshException {
+        final Account account = getAccount(accountId, context);
+
+        // Recompute all invoices and invoice items
+        final Collection<BusinessSubscriptionTransitionModelDao> bsts = createBusinessSubscriptionTransitions(account, context);
+
+        sqlDao.inTransaction(new Transaction<Void, BusinessAnalyticsSqlDao>() {
+            @Override
+            public Void inTransaction(final BusinessAnalyticsSqlDao transactional, final TransactionStatus status) throws Exception {
+                updateInTransaction(bsts, transactional, context);
+                return null;
+            }
+        });
+    }
+
+    public void updateInTransaction(final Collection<BusinessSubscriptionTransitionModelDao> bsts, final BusinessAnalyticsSqlDao transactional, final CallContext context) {
+        if (bsts.size() == 0) {
+            return;
+        }
+
+        final BusinessSubscriptionTransitionModelDao firstBst = bsts.iterator().next();
+        transactional.deleteByAccountRecordId(firstBst.getTableName(), firstBst.getAccountRecordId(), firstBst.getTenantRecordId(), context);
+
+        for (final BusinessSubscriptionTransitionModelDao bst : bsts) {
+            transactional.create(bst.getTableName(), bst, context);
+        }
+    }
+
+    private Collection<BusinessSubscriptionTransitionModelDao> createBusinessSubscriptionTransitions(final Account account, final CallContext context) throws AnalyticsRefreshException {
+        final Collection<BusinessSubscriptionTransitionModelDao> bsts = new LinkedList<BusinessSubscriptionTransitionModelDao>();
+
+        final List<SubscriptionBundle> bundles = getSubscriptionBundlesForAccount(account.getId(), context);
+        for (final SubscriptionBundle bundle : bundles) {
+            final Collection<Subscription> subscriptions = getSubscriptionsForBundle(bundle.getId(), context);
+            for (final Subscription subscription : subscriptions) {
+                final List<SubscriptionTransition> transitions = subscription.getAllTransitions();
+
+                BusinessSubscription prevNextSubscription = null;
+
+                // Ordered for us by entitlement
+                for (final SubscriptionTransition transition : transitions) {
+                    final BusinessSubscription nextSubscription = getBusinessSubscriptionFromTransition(account, transition);
+                    final BusinessSubscriptionTransitionModelDao bst = createBusinessSubscriptionTransition(account,
+                                                                                                            bundle,
+                                                                                                            transition,
+                                                                                                            prevNextSubscription,
+                                                                                                            nextSubscription,
+                                                                                                            context);
+                    if (bst != null) {
+                        bsts.add(bst);
+                        prevNextSubscription = nextSubscription;
+                    }
+                }
+            }
+        }
+
+        return bsts;
+    }
+
+    private BusinessSubscriptionTransitionModelDao createBusinessSubscriptionTransition(final Account account,
+                                                                                        final SubscriptionBundle subscriptionBundle,
+                                                                                        final SubscriptionTransition subscriptionTransition,
+                                                                                        @Nullable final BusinessSubscription prevNextSubscription,
+                                                                                        final BusinessSubscription nextSubscription,
+                                                                                        final CallContext context) throws AnalyticsRefreshException {
+        final BusinessSubscriptionEvent businessEvent = BusinessSubscriptionEvent.fromTransition(subscriptionTransition);
+        if (businessEvent == null) {
+            return null;
+        }
+
+        final Long subscriptionEventRecordId = getSubscriptionEventRecordId(subscriptionTransition.getNextEventId(), context);
+        final AuditLog creationAuditLog = getSubscriptionEventCreationAuditLog(subscriptionTransition.getNextEventId(), context);
+
+        final Long accountRecordId = getAccountRecordId(account.getId(), context);
+        final Long tenantRecordId = getTenantRecordId(context);
+
+        return new BusinessSubscriptionTransitionModelDao(account,
+                                                          accountRecordId,
+                                                          subscriptionBundle,
+                                                          subscriptionTransition,
+                                                          subscriptionEventRecordId,
+                                                          subscriptionTransition.getRequestedTransitionTime(),
+                                                          businessEvent,
+                                                          prevNextSubscription,
+                                                          nextSubscription,
+                                                          creationAuditLog,
+                                                          tenantRecordId);
+    }
+
+    private BusinessSubscription getBusinessSubscriptionFromTransition(final Account account, final SubscriptionTransition subscriptionTransition) {
+        return new BusinessSubscription(subscriptionTransition.getNextPlan(),
+                                        subscriptionTransition.getNextPhase(),
+                                        subscriptionTransition.getNextPriceList(),
+                                        account.getCurrency(),
+                                        subscriptionTransition.getEffectiveTransitionTime(),
+                                        subscriptionTransition.getNextState());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessTagDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessTagDao.java
new file mode 100644
index 0000000..5d044f3
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/BusinessTagDao.java
@@ -0,0 +1,96 @@
+/*
+ * 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.dao;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.UUID;
+
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsRefreshException;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessTagModelDao;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
+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 BusinessTagDao extends BusinessAnalyticsDaoBase {
+
+    public BusinessTagDao(final OSGIKillbillLogService logService,
+                          final OSGIKillbillAPI osgiKillbillAPI,
+                          final OSGIKillbillDataSource osgiKillbillDataSource) {
+        super(logService, osgiKillbillAPI, osgiKillbillDataSource);
+    }
+
+    public void update(final UUID accountId, final CallContext context) throws AnalyticsRefreshException {
+        final Account account = getAccount(accountId, context);
+
+        final Collection<BusinessTagModelDao> tagModelDaos = createBusinessTags(account, context);
+
+        sqlDao.inTransaction(new Transaction<Void, BusinessAnalyticsSqlDao>() {
+            @Override
+            public Void inTransaction(final BusinessAnalyticsSqlDao transactional, final TransactionStatus status) throws Exception {
+                updateInTransaction(tagModelDaos, transactional, context);
+                return null;
+            }
+        });
+    }
+
+    private void updateInTransaction(final Collection<BusinessTagModelDao> tagModelDaos, final BusinessAnalyticsSqlDao transactional, final CallContext context) {
+        if (tagModelDaos.size() == 0) {
+            return;
+        }
+
+        // We assume all tagModelDaos are for a single type
+        final BusinessTagModelDao firstTagModelDao = tagModelDaos.iterator().next();
+        transactional.deleteByAccountRecordId(firstTagModelDao.getTableName(), firstTagModelDao.getAccountRecordId(), firstTagModelDao.getTenantRecordId(), context);
+
+        for (final BusinessTagModelDao tagModelDao : tagModelDaos) {
+            transactional.create(tagModelDao.getTableName(), tagModelDao, context);
+        }
+    }
+
+    private Collection<BusinessTagModelDao> createBusinessTags(final Account account, final CallContext context) throws AnalyticsRefreshException {
+        final Long accountRecordId = getAccountRecordId(account.getId(), context);
+        final Long tenantRecordId = getTenantRecordId(context);
+
+        final Collection<Tag> tags = getTagsForAccount(account.getId(), context);
+
+        final Collection<BusinessTagModelDao> tagModelDaos = new LinkedList<BusinessTagModelDao>();
+        for (final Tag tag : tags) {
+            final Long tagRecordId = getTagRecordId(tag.getId(), context);
+            final TagDefinition tagDefinition = getTagDefinition(tag.getTagDefinitionId(), context);
+            final AuditLog creationAuditLog = getTagCreationAuditLog(tag.getId(), context);
+            final BusinessTagModelDao tagModelDao = BusinessTagModelDao.create(account,
+                                                                               accountRecordId,
+                                                                               tag,
+                                                                               tagRecordId,
+                                                                               tagDefinition,
+                                                                               creationAuditLog,
+                                                                               tenantRecordId);
+            tagModelDaos.add(tagModelDao);
+        }
+
+        return tagModelDaos;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessAccountModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessAccountModelDao.java
new file mode 100644
index 0000000..03f1cd6
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessAccountModelDao.java
@@ -0,0 +1,399 @@
+/*
+ * 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.dao.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.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 String email;
+    private Integer firstNameLength;
+    private String currency;
+    private Integer billingCycleDayLocal;
+    private UUID paymentMethodId;
+    private String timeZone;
+    private String locale;
+    private String address1;
+    private String address2;
+    private String companyName;
+    private String city;
+    private String stateOrProvince;
+    private String country;
+    private String postalCode;
+    private String phone;
+    private Boolean migrated;
+    private Boolean notifiedForInvoices;
+    private DateTime updatedDate;
+    private BigDecimal balance;
+    private LocalDate lastInvoiceDate;
+    private DateTime lastPaymentDate;
+    private String lastPaymentStatus;
+
+    public BusinessAccountModelDao() { /* When reading from the database */ }
+
+    public BusinessAccountModelDao(final String email,
+                                   final Integer firstNameLength,
+                                   final String currency,
+                                   final Integer billingCycleDayLocal,
+                                   final UUID paymentMethodId,
+                                   final String timeZone,
+                                   final String locale,
+                                   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 DateTime updatedDate,
+                                   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.paymentMethodId = paymentMethodId;
+        this.timeZone = timeZone;
+        this.locale = locale;
+        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.migrated = isMigrated;
+        this.notifiedForInvoices = notifiedForInvoices;
+        this.updatedDate = updatedDate;
+        this.balance = balance;
+        this.lastInvoiceDate = lastInvoiceDate;
+        this.lastPaymentDate = lastPaymentDate;
+        this.lastPaymentStatus = lastPaymentStatus;
+    }
+
+    public BusinessAccountModelDao(final Account account,
+                                   final Long accountRecordId,
+                                   final BigDecimal balance,
+                                   @Nullable final Invoice lastInvoice,
+                                   @Nullable final Payment lastPayment,
+                                   final AuditLog creationAuditLog,
+                                   final Long tenantRecordId) {
+        this(account.getEmail(),
+             account.getFirstNameLength(),
+             account.getCurrency() == null ? null : account.getCurrency().toString(),
+             account.getBillCycleDayLocal(),
+             account.getPaymentMethodId(),
+             account.getTimeZone() == null ? null : account.getTimeZone().toString(),
+             account.getLocale(),
+             account.getAddress1(),
+             account.getAddress2(),
+             account.getCompanyName(),
+             account.getCity(),
+             account.getStateOrProvince(),
+             account.getCountry(),
+             account.getPostalCode(),
+             account.getPhone(),
+             account.isMigrated(),
+             account.isNotifiedForInvoices(),
+             account.getUpdatedDate(),
+             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(),
+             accountRecordId,
+             tenantRecordId);
+    }
+
+    @Override
+    public String getTableName() {
+        return ACCOUNTS_TABLE_NAME;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public Integer getFirstNameLength() {
+        return firstNameLength;
+    }
+
+    public String getCurrency() {
+        return currency;
+    }
+
+    public Integer getBillingCycleDayLocal() {
+        return billingCycleDayLocal;
+    }
+
+    public UUID getPaymentMethodId() {
+        return paymentMethodId;
+    }
+
+    public String getTimeZone() {
+        return timeZone;
+    }
+
+    public String getLocale() {
+        return locale;
+    }
+
+    public String getAddress1() {
+        return address1;
+    }
+
+    public String getAddress2() {
+        return address2;
+    }
+
+    public String getCompanyName() {
+        return companyName;
+    }
+
+    public String getCity() {
+        return city;
+    }
+
+    public String getStateOrProvince() {
+        return stateOrProvince;
+    }
+
+    public String getCountry() {
+        return country;
+    }
+
+    public String getPostalCode() {
+        return postalCode;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public Boolean getMigrated() {
+        return migrated;
+    }
+
+    public Boolean getNotifiedForInvoices() {
+        return notifiedForInvoices;
+    }
+
+    public DateTime getUpdatedDate() {
+        return updatedDate;
+    }
+
+    public BigDecimal getBalance() {
+        return balance;
+    }
+
+    public LocalDate getLastInvoiceDate() {
+        return lastInvoiceDate;
+    }
+
+    public DateTime getLastPaymentDate() {
+        return lastPaymentDate;
+    }
+
+    public String getLastPaymentStatus() {
+        return lastPaymentStatus;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessAccountModelDao");
+        sb.append("{email='").append(email).append('\'');
+        sb.append(", firstNameLength=").append(firstNameLength);
+        sb.append(", currency='").append(currency).append('\'');
+        sb.append(", billingCycleDayLocal=").append(billingCycleDayLocal);
+        sb.append(", paymentMethodId=").append(paymentMethodId);
+        sb.append(", timeZone='").append(timeZone).append('\'');
+        sb.append(", locale='").append(locale).append('\'');
+        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(", migrated=").append(migrated);
+        sb.append(", notifiedForInvoices=").append(notifiedForInvoices);
+        sb.append(", updatedDate=").append(updatedDate);
+        sb.append(", balance=").append(balance);
+        sb.append(", lastInvoiceDate=").append(lastInvoiceDate);
+        sb.append(", lastPaymentDate=").append(lastPaymentDate);
+        sb.append(", lastPaymentStatus='").append(lastPaymentStatus).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 BusinessAccountModelDao that = (BusinessAccountModelDao) o;
+
+        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 (balance != null ? balance.compareTo(that.balance) != 0 : that.balance != null) {
+            return false;
+        }
+        if (billingCycleDayLocal != null ? !billingCycleDayLocal.equals(that.billingCycleDayLocal) : that.billingCycleDayLocal != null) {
+            return false;
+        }
+        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 (email != null ? !email.equals(that.email) : that.email != null) {
+            return false;
+        }
+        if (firstNameLength != null ? !firstNameLength.equals(that.firstNameLength) : that.firstNameLength != 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 (lastPaymentStatus != null ? !lastPaymentStatus.equals(that.lastPaymentStatus) : that.lastPaymentStatus != null) {
+            return false;
+        }
+        if (locale != null ? !locale.equals(that.locale) : that.locale != null) {
+            return false;
+        }
+        if (migrated != null ? !migrated.equals(that.migrated) : that.migrated != null) {
+            return false;
+        }
+        if (notifiedForInvoices != null ? !notifiedForInvoices.equals(that.notifiedForInvoices) : that.notifiedForInvoices != null) {
+            return false;
+        }
+        if (paymentMethodId != null ? !paymentMethodId.equals(that.paymentMethodId) : that.paymentMethodId != null) {
+            return false;
+        }
+        if (phone != null ? !phone.equals(that.phone) : that.phone != null) {
+            return false;
+        }
+        if (postalCode != null ? !postalCode.equals(that.postalCode) : that.postalCode != null) {
+            return false;
+        }
+        if (stateOrProvince != null ? !stateOrProvince.equals(that.stateOrProvince) : that.stateOrProvince != null) {
+            return false;
+        }
+        if (timeZone != null ? !timeZone.equals(that.timeZone) : that.timeZone != null) {
+            return false;
+        }
+        if (updatedDate != null ? !updatedDate.equals(that.updatedDate) : that.updatedDate != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        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 + (paymentMethodId != null ? paymentMethodId.hashCode() : 0);
+        result = 31 * result + (timeZone != null ? timeZone.hashCode() : 0);
+        result = 31 * result + (locale != null ? locale.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 + (migrated != null ? migrated.hashCode() : 0);
+        result = 31 * result + (notifiedForInvoices != null ? notifiedForInvoices.hashCode() : 0);
+        result = 31 * result + (updatedDate != null ? updatedDate.hashCode() : 0);
+        result = 31 * result + (balance != null ? balance.hashCode() : 0);
+        result = 31 * result + (lastInvoiceDate != null ? lastInvoiceDate.hashCode() : 0);
+        result = 31 * result + (lastPaymentDate != null ? lastPaymentDate.hashCode() : 0);
+        result = 31 * result + (lastPaymentStatus != null ? lastPaymentStatus.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessAccountTagModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessAccountTagModelDao.java
new file mode 100644
index 0000000..884a8a8
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessAccountTagModelDao.java
@@ -0,0 +1,48 @@
+/*
+ * 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.dao.model;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
+
+public class BusinessAccountTagModelDao extends BusinessTagModelDao {
+
+    public BusinessAccountTagModelDao() { /* When reading from the database */ }
+
+    public BusinessAccountTagModelDao(final Account account,
+                                      final Long accountRecordId,
+                                      final Tag tag,
+                                      final Long tagRecordId,
+                                      final TagDefinition tagDefinition,
+                                      final AuditLog creationAuditLog,
+                                      final Long tenantRecordId) {
+        super(account,
+              accountRecordId,
+              tag,
+              tagRecordId,
+              tagDefinition,
+              creationAuditLog,
+              tenantRecordId);
+    }
+
+    @Override
+    public String getTableName() {
+        return ACCOUNT_TAGS_TABLE_NAME;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessFieldModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessFieldModelDao.java
new file mode 100644
index 0000000..e8d8245
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessFieldModelDao.java
@@ -0,0 +1,179 @@
+/*
+ * 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.dao.model;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.customfield.CustomField;
+
+public abstract class BusinessFieldModelDao extends BusinessModelDaoBase {
+
+    protected static final String ACCOUNT_FIELDS_TABLE_NAME = "bac_fields";
+    protected static final String INVOICE_FIELDS_TABLE_NAME = "bin_fields";
+    protected static final String INVOICE_PAYMENT_FIELDS_TABLE_NAME = "bip_fields";
+
+    public static final String[] ALL_FIELDS_TABLE_NAMES = new String[]{ACCOUNT_FIELDS_TABLE_NAME, INVOICE_FIELDS_TABLE_NAME, INVOICE_PAYMENT_FIELDS_TABLE_NAME};
+
+    private Long customFieldRecordId;
+    private String name;
+    private String value;
+
+    public static BusinessFieldModelDao create(final Account account,
+                                               final Long accountRecordId,
+                                               final CustomField customField,
+                                               final Long customFieldRecordId,
+                                               final AuditLog creationAuditLog,
+                                               final Long tenantRecordId) {
+        if (ObjectType.ACCOUNT.equals(customField.getObjectType())) {
+            return new BusinessAccountFieldModelDao(account,
+                                                    accountRecordId,
+                                                    customField,
+                                                    customFieldRecordId,
+                                                    creationAuditLog,
+                                                    tenantRecordId);
+        } else if (ObjectType.INVOICE_PAYMENT.equals(customField.getObjectType())) {
+            return new BusinessInvoiceFieldModelDao(account,
+                                                    accountRecordId,
+                                                    customField,
+                                                    customFieldRecordId,
+                                                    creationAuditLog,
+                                                    tenantRecordId);
+        } else if (ObjectType.INVOICE.equals(customField.getObjectType())) {
+            return new BusinessInvoicePaymentFieldModelDao(account,
+                                                           accountRecordId,
+                                                           customField,
+                                                           customFieldRecordId,
+                                                           creationAuditLog,
+                                                           tenantRecordId);
+        } else {
+            // We don't care
+            return null;
+        }
+    }
+
+    public BusinessFieldModelDao() { /* When reading from the database */ }
+
+    public 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 BusinessFieldModelDao(final Account account,
+                                 final Long accountRecordId,
+                                 final CustomField customField,
+                                 final Long customFieldRecordId,
+                                 final AuditLog creationAuditLog,
+                                 final Long tenantRecordId) {
+        this(customFieldRecordId,
+             customField.getFieldName(),
+             customField.getFieldValue(),
+             customField.getCreatedDate(),
+             creationAuditLog.getUserName(),
+             creationAuditLog.getReasonCode(),
+             creationAuditLog.getComment(),
+             account.getId(),
+             account.getName(),
+             account.getExternalKey(),
+             accountRecordId,
+             tenantRecordId);
+    }
+
+    public Long getCustomFieldRecordId() {
+        return customFieldRecordId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    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/dao/model/BusinessInvoiceAdjustmentModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceAdjustmentModelDao.java
new file mode 100644
index 0000000..14b70f7
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceAdjustmentModelDao.java
@@ -0,0 +1,61 @@
+/*
+ * 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.dao.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() { /* When reading from the database */ }
+
+    public BusinessInvoiceAdjustmentModelDao(final Account account,
+                                             final Long accountRecordId,
+                                             final Invoice invoice,
+                                             final InvoiceItem invoiceItem,
+                                             final Long invoiceItemRecordId,
+                                             final Long secondInvoiceItemRecordId,
+                                             @Nullable final SubscriptionBundle bundle,
+                                             @Nullable final Plan plan,
+                                             @Nullable final PlanPhase planPhase,
+                                             final AuditLog creationAuditLog,
+                                             final Long tenantRecordId) {
+        super(account,
+              accountRecordId,
+              invoice,
+              invoiceItem,
+              invoiceItemRecordId,
+              secondInvoiceItemRecordId,
+              bundle,
+              plan,
+              planPhase,
+              creationAuditLog,
+              tenantRecordId);
+    }
+
+    @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/dao/model/BusinessInvoiceItemAdjustmentModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemAdjustmentModelDao.java
new file mode 100644
index 0000000..3bcc420
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemAdjustmentModelDao.java
@@ -0,0 +1,61 @@
+/*
+ * 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.dao.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() { /* When reading from the database */ }
+
+    public BusinessInvoiceItemAdjustmentModelDao(final Account account,
+                                                 final Long accountRecordId,
+                                                 final Invoice invoice,
+                                                 final InvoiceItem invoiceItem,
+                                                 final Long invoiceItemRecordId,
+                                                 final Long secondInvoiceItemRecordId,
+                                                 @Nullable final SubscriptionBundle bundle,
+                                                 @Nullable final Plan plan,
+                                                 @Nullable final PlanPhase planPhase,
+                                                 final AuditLog creationAuditLog,
+                                                 final Long tenantRecordId) {
+        super(account,
+              accountRecordId,
+              invoice,
+              invoiceItem,
+              invoiceItemRecordId,
+              secondInvoiceItemRecordId,
+              bundle,
+              plan,
+              planPhase,
+              creationAuditLog,
+              tenantRecordId);
+    }
+
+    @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/dao/model/BusinessInvoiceItemBaseModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemBaseModelDao.java
new file mode 100644
index 0000000..9fac8f8
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemBaseModelDao.java
@@ -0,0 +1,554 @@
+/*
+ * 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.dao.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.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 Long invoiceItemRecordId;
+    private Long secondInvoiceItemRecordId;
+    private UUID itemId;
+    private UUID invoiceId;
+    private Integer invoiceNumber;
+    private DateTime invoiceCreatedDate;
+    private LocalDate invoiceDate;
+    private LocalDate invoiceTargetDate;
+    private String invoiceCurrency;
+    private BigDecimal invoiceBalance;
+    private BigDecimal invoiceAmountPaid;
+    private BigDecimal invoiceAmountCharged;
+    private BigDecimal invoiceOriginalAmountCharged;
+    private BigDecimal invoiceAmountCredited;
+    private String itemType;
+    private Boolean revenueRecognizable;
+    private String bundleExternalKey;
+    private String productName;
+    private String productType;
+    private String productCategory;
+    private String slug;
+    private String phase;
+    private String billingPeriod;
+    private LocalDate startDate;
+    private LocalDate endDate;
+    private BigDecimal amount;
+    private String currency;
+    private UUID linkedItemId;
+
+    public static BusinessInvoiceItemBaseModelDao create(final Account account,
+                                                         final Long accountRecordId,
+                                                         final Invoice invoice,
+                                                         final InvoiceItem invoiceItem,
+                                                         final Long invoiceItemRecordId,
+                                                         final Long secondInvoiceItemRecordId,
+                                                         @Nullable final SubscriptionBundle bundle,
+                                                         @Nullable final Plan plan,
+                                                         @Nullable final PlanPhase planPhase,
+                                                         final AuditLog creationAuditLog,
+                                                         final Long tenantRecordId) {
+        if (InvoiceItemType.REFUND_ADJ.equals(invoiceItem.getInvoiceItemType())) {
+            return new BusinessInvoiceAdjustmentModelDao(account,
+                                                         accountRecordId,
+                                                         invoice,
+                                                         invoiceItem,
+                                                         invoiceItemRecordId,
+                                                         secondInvoiceItemRecordId,
+                                                         bundle,
+                                                         plan,
+                                                         planPhase,
+                                                         creationAuditLog,
+                                                         tenantRecordId);
+        } else if (InvoiceItemType.EXTERNAL_CHARGE.equals(invoiceItem.getInvoiceItemType()) ||
+                   InvoiceItemType.FIXED.equals(invoiceItem.getInvoiceItemType()) ||
+                   InvoiceItemType.RECURRING.equals(invoiceItem.getInvoiceItemType())) {
+            return new BusinessInvoiceItemModelDao(account,
+                                                   accountRecordId,
+                                                   invoice,
+                                                   invoiceItem,
+                                                   invoiceItemRecordId,
+                                                   secondInvoiceItemRecordId,
+                                                   bundle,
+                                                   plan,
+                                                   planPhase,
+                                                   creationAuditLog,
+                                                   tenantRecordId);
+        } else if (InvoiceItemType.ITEM_ADJ.equals(invoiceItem.getInvoiceItemType())) {
+            return new BusinessInvoiceItemAdjustmentModelDao(account,
+                                                             accountRecordId,
+                                                             invoice,
+                                                             invoiceItem,
+                                                             invoiceItemRecordId,
+                                                             secondInvoiceItemRecordId,
+                                                             bundle,
+                                                             plan,
+                                                             planPhase,
+                                                             creationAuditLog,
+                                                             tenantRecordId);
+        } else if (InvoiceItemType.CBA_ADJ.equals(invoiceItem.getInvoiceItemType()) ||
+                   InvoiceItemType.CREDIT_ADJ.equals(invoiceItem.getInvoiceItemType())) {
+            return new BusinessInvoiceItemCreditModelDao(account,
+                                                         accountRecordId,
+                                                         invoice,
+                                                         invoiceItem,
+                                                         invoiceItemRecordId,
+                                                         secondInvoiceItemRecordId,
+                                                         bundle,
+                                                         plan,
+                                                         planPhase,
+                                                         creationAuditLog,
+                                                         tenantRecordId);
+        } else {
+            // We don't care
+            return null;
+        }
+    }
+
+    public BusinessInvoiceItemBaseModelDao() { /* When reading from the database */ }
+
+    public BusinessInvoiceItemBaseModelDao(final Long invoiceItemRecordId,
+                                           final Long secondInvoiceItemRecordId,
+                                           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 revenueRecognizable,
+                                           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 String 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.secondInvoiceItemRecordId = secondInvoiceItemRecordId;
+        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.revenueRecognizable = revenueRecognizable;
+        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;
+    }
+
+    public BusinessInvoiceItemBaseModelDao(final Account account,
+                                           final Long accountRecordId,
+                                           final Invoice invoice,
+                                           final InvoiceItem invoiceItem,
+                                           final Long invoiceItemRecordId,
+                                           final Long secondInvoiceItemRecordId,
+                                           @Nullable final SubscriptionBundle bundle,
+                                           @Nullable final Plan plan,
+                                           @Nullable final PlanPhase planPhase,
+                                           final AuditLog creationAuditLog,
+                                           final Long tenantRecordId) {
+        this(invoiceItemRecordId,
+             secondInvoiceItemRecordId,
+             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(),
+             invoice.getOriginalChargedAmount(),
+             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() == null ? null : invoiceItem.getCurrency().toString(),
+             invoiceItem.getLinkedItemId(),
+             invoiceItem.getCreatedDate(),
+             creationAuditLog.getUserName(),
+             creationAuditLog.getReasonCode(),
+             creationAuditLog.getComment(),
+             account.getId(),
+             account.getName(),
+             account.getExternalKey(),
+             accountRecordId,
+             tenantRecordId);
+    }
+
+    public Long getInvoiceItemRecordId() {
+        return invoiceItemRecordId;
+    }
+
+    public Long getSecondInvoiceItemRecordId() {
+        return secondInvoiceItemRecordId;
+    }
+
+    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 getRevenueRecognizable() {
+        return revenueRecognizable;
+    }
+
+    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 String 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(", secondInvoiceItemRecordId=").append(secondInvoiceItemRecordId);
+        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(", revenueRecognizable=").append(revenueRecognizable);
+        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 (revenueRecognizable != null ? !revenueRecognizable.equals(that.revenueRecognizable) : that.revenueRecognizable != null) {
+            return false;
+        }
+        if (secondInvoiceItemRecordId != null ? !secondInvoiceItemRecordId.equals(that.secondInvoiceItemRecordId) : that.secondInvoiceItemRecordId != 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 + (secondInvoiceItemRecordId != null ? secondInvoiceItemRecordId.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 + (revenueRecognizable != null ? revenueRecognizable.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/dao/model/BusinessInvoiceItemCreditModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemCreditModelDao.java
new file mode 100644
index 0000000..6115d34
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemCreditModelDao.java
@@ -0,0 +1,61 @@
+/*
+ * 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.dao.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() { /* When reading from the database */ }
+
+    public BusinessInvoiceItemCreditModelDao(final Account account,
+                                             final Long accountRecordId,
+                                             final Invoice invoice,
+                                             final InvoiceItem invoiceItem,
+                                             final Long invoiceItemRecordId,
+                                             final Long secondInvoiceItemRecordId,
+                                             @Nullable final SubscriptionBundle bundle,
+                                             @Nullable final Plan plan,
+                                             @Nullable final PlanPhase planPhase,
+                                             final AuditLog creationAuditLogs,
+                                             final Long tenantRecordId) {
+        super(account,
+              accountRecordId,
+              invoice,
+              invoiceItem,
+              invoiceItemRecordId,
+              secondInvoiceItemRecordId,
+              bundle,
+              plan,
+              planPhase,
+              creationAuditLogs,
+              tenantRecordId);
+    }
+
+    @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/dao/model/BusinessInvoiceItemModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemModelDao.java
new file mode 100644
index 0000000..715d452
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceItemModelDao.java
@@ -0,0 +1,61 @@
+/*
+ * 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.dao.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 BusinessInvoiceItemModelDao extends BusinessInvoiceItemBaseModelDao {
+
+    public BusinessInvoiceItemModelDao() { /* When reading from the database */ }
+
+    public BusinessInvoiceItemModelDao(final Account account,
+                                       final Long accountRecordId,
+                                       final Invoice invoice,
+                                       final InvoiceItem invoiceItem,
+                                       final Long invoiceItemRecordId,
+                                       final Long secondInvoiceItemRecordId,
+                                       @Nullable final SubscriptionBundle bundle,
+                                       @Nullable final Plan plan,
+                                       @Nullable final PlanPhase planPhase,
+                                       final AuditLog creationAuditLog,
+                                       final Long tenantRecordId) {
+        super(account,
+              accountRecordId,
+              invoice,
+              invoiceItem,
+              invoiceItemRecordId,
+              secondInvoiceItemRecordId,
+              bundle,
+              plan,
+              planPhase,
+              creationAuditLog,
+              tenantRecordId);
+    }
+
+    @Override
+    public String getTableName() {
+        return INVOICE_ITEMS_TABLE_NAME;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceModelDao.java
new file mode 100644
index 0000000..f505795
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoiceModelDao.java
@@ -0,0 +1,252 @@
+/*
+ * 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.dao.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.invoice.api.Invoice;
+import com.ning.billing.util.audit.AuditLog;
+
+public class BusinessInvoiceModelDao extends BusinessModelDaoBase {
+
+    public static final String INVOICES_TABLE_NAME = "bin";
+
+    private Long invoiceRecordId;
+    private UUID invoiceId;
+    private Integer invoiceNumber;
+    private LocalDate invoiceDate;
+    private LocalDate targetDate;
+    private String currency;
+    private BigDecimal balance;
+    private BigDecimal amountPaid;
+    private BigDecimal amountCharged;
+    private BigDecimal originalAmountCharged;
+    private BigDecimal amountCredited;
+
+    public BusinessInvoiceModelDao() { /* When reading from the database */ }
+
+    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 Account account,
+                                   final Long accountRecordId,
+                                   final Invoice invoice,
+                                   final Long invoiceRecordId,
+                                   final AuditLog creationAuditLog,
+                                   final Long tenantRecordId) {
+        this(invoiceRecordId,
+             invoice.getId(),
+             invoice.getInvoiceNumber(),
+             invoice.getInvoiceDate(),
+             invoice.getTargetDate(),
+             invoice.getCurrency() == null ? null : invoice.getCurrency().toString(),
+             invoice.getBalance(),
+             invoice.getPaidAmount(),
+             invoice.getChargedAmount(),
+             invoice.getOriginalChargedAmount(),
+             invoice.getCreditAdjAmount(),
+             invoice.getCreatedDate(),
+             creationAuditLog.getUserName(),
+             creationAuditLog.getReasonCode(),
+             creationAuditLog.getComment(),
+             account.getId(),
+             account.getName(),
+             account.getExternalKey(),
+             accountRecordId,
+             tenantRecordId);
+    }
+
+    @Override
+    public String getTableName() {
+        return INVOICES_TABLE_NAME;
+    }
+
+    public Long getInvoiceRecordId() {
+        return invoiceRecordId;
+    }
+
+    public UUID getInvoiceId() {
+        return invoiceId;
+    }
+
+    public Integer getInvoiceNumber() {
+        return invoiceNumber;
+    }
+
+    public LocalDate getInvoiceDate() {
+        return invoiceDate;
+    }
+
+    public LocalDate getTargetDate() {
+        return targetDate;
+    }
+
+    public String getCurrency() {
+        return currency;
+    }
+
+    public BigDecimal getBalance() {
+        return balance;
+    }
+
+    public BigDecimal getAmountPaid() {
+        return amountPaid;
+    }
+
+    public BigDecimal getAmountCharged() {
+        return amountCharged;
+    }
+
+    public BigDecimal getOriginalAmountCharged() {
+        return originalAmountCharged;
+    }
+
+    public BigDecimal getAmountCredited() {
+        return amountCredited;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessInvoiceModelDao");
+        sb.append("{invoiceRecordId=").append(invoiceRecordId);
+        sb.append(", invoiceId=").append(invoiceId);
+        sb.append(", invoiceNumber=").append(invoiceNumber);
+        sb.append(", invoiceDate=").append(invoiceDate);
+        sb.append(", targetDate=").append(targetDate);
+        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();
+    }
+
+    @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 BusinessInvoiceModelDao that = (BusinessInvoiceModelDao) o;
+
+        if (amountCharged != null ? !amountCharged.equals(that.amountCharged) : that.amountCharged != null) {
+            return false;
+        }
+        if (amountCredited != null ? !amountCredited.equals(that.amountCredited) : that.amountCredited != null) {
+            return false;
+        }
+        if (amountPaid != null ? !amountPaid.equals(that.amountPaid) : that.amountPaid != null) {
+            return false;
+        }
+        if (balance != null ? !balance.equals(that.balance) : that.balance != null) {
+            return false;
+        }
+        if (currency != null ? !currency.equals(that.currency) : that.currency != 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 (invoiceRecordId != null ? !invoiceRecordId.equals(that.invoiceRecordId) : that.invoiceRecordId != null) {
+            return false;
+        }
+        if (originalAmountCharged != null ? !originalAmountCharged.equals(that.originalAmountCharged) : that.originalAmountCharged != null) {
+            return false;
+        }
+        if (targetDate != null ? !targetDate.equals(that.targetDate) : that.targetDate != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        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 + (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/dao/model/BusinessInvoicePaymentBaseModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoicePaymentBaseModelDao.java
new file mode 100644
index 0000000..8bcd54b
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoicePaymentBaseModelDao.java
@@ -0,0 +1,672 @@
+/*
+ * 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.dao.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.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.payment.api.Refund;
+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 Long invoicePaymentRecordId;
+    private UUID invoicePaymentId;
+    private UUID invoiceId;
+    private Integer invoiceNumber;
+    private DateTime invoiceCreatedDate;
+    private LocalDate invoiceDate;
+    private LocalDate invoiceTargetDate;
+    private String invoiceCurrency;
+    private BigDecimal invoiceBalance;
+    private BigDecimal invoiceAmountPaid;
+    private BigDecimal invoiceAmountCharged;
+    private BigDecimal invoiceOriginalAmountCharged;
+    private BigDecimal invoiceAmountCredited;
+    private String invoicePaymentType;
+    private Long paymentNumber;
+    private UUID linkedInvoicePaymentId;
+    private BigDecimal amount;
+    private String currency;
+    private DateTime pluginCreatedDate;
+    private DateTime pluginEffectiveDate;
+    private String pluginStatus;
+    private String pluginGatewayError;
+    private String pluginGatewayErrorCode;
+    private String pluginFirstReferenceId;
+    private String pluginSecondReferenceId;
+    private String pluginPmId;
+    private Boolean pluginPmIsDefault;
+    private String pluginPmType;
+    private String pluginPmCcName;
+    private String pluginPmCcType;
+    private String pluginPmCcExpirationMonth;
+    private String pluginPmCcExpirationYear;
+    private String pluginPmCcLast4;
+    private String pluginPmAddress1;
+    private String pluginPmAddress2;
+    private String pluginPmCity;
+    private String pluginPmState;
+    private String pluginPmZip;
+    private String pluginPmCountry;
+
+    public static BusinessInvoicePaymentBaseModelDao create(final Account account,
+                                                            final Long accountRecordId,
+                                                            final Invoice invoice,
+                                                            final InvoicePayment invoicePayment,
+                                                            final Long invoicePaymentRecordId,
+                                                            final Payment payment,
+                                                            final Refund refund,
+                                                            final PaymentMethod paymentMethod,
+                                                            final AuditLog creationAuditLog,
+                                                            final Long tenantRecordId) {
+        if (invoicePayment.getType().equals(InvoicePaymentType.REFUND)) {
+            return new BusinessInvoicePaymentRefundModelDao(account,
+                                                            accountRecordId,
+                                                            invoice,
+                                                            invoicePayment,
+                                                            invoicePaymentRecordId,
+                                                            payment,
+                                                            refund,
+                                                            paymentMethod,
+                                                            creationAuditLog,
+                                                            tenantRecordId);
+        } else if (invoicePayment.getType().equals(InvoicePaymentType.CHARGED_BACK)) {
+            return new BusinessInvoicePaymentChargebackModelDao(account,
+                                                                accountRecordId,
+                                                                invoice,
+                                                                invoicePayment,
+                                                                invoicePaymentRecordId,
+                                                                payment,
+                                                                refund,
+                                                                paymentMethod,
+                                                                creationAuditLog,
+                                                                tenantRecordId);
+        } else {
+            return new BusinessInvoicePaymentModelDao(account,
+                                                      accountRecordId,
+                                                      invoice,
+                                                      invoicePayment,
+                                                      invoicePaymentRecordId,
+                                                      payment,
+                                                      refund,
+                                                      paymentMethod,
+                                                      creationAuditLog,
+                                                      tenantRecordId);
+        }
+    }
+
+    public BusinessInvoicePaymentBaseModelDao() { /* When reading from the database */ }
+
+    public 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 String currency,
+                                              final DateTime pluginCreatedDate,
+                                              final DateTime pluginEffectiveDate,
+                                              final String pluginStatus,
+                                              final String pluginGatewayError,
+                                              final String pluginGatewayErrorCode,
+                                              final String pluginFirstReferenceId,
+                                              final String pluginSecondReferenceId,
+                                              final String pluginPmId,
+                                              final Boolean pluginPmIsDefault,
+                                              final String pluginPmType,
+                                              final String pluginPmCcName,
+                                              final String pluginPmCcType,
+                                              final String pluginPmCcExpirationMonth,
+                                              final String pluginPmCcExpirationYear,
+                                              final String pluginPmCcLast4,
+                                              final String pluginPmAddress1,
+                                              final String pluginPmAddress2,
+                                              final String pluginPmCity,
+                                              final String pluginPmState,
+                                              final String pluginPmZip,
+                                              final String pluginPmCountry,
+                                              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;
+        this.pluginCreatedDate = pluginCreatedDate;
+        this.pluginEffectiveDate = pluginEffectiveDate;
+        this.pluginStatus = pluginStatus;
+        this.pluginGatewayError = pluginGatewayError;
+        this.pluginGatewayErrorCode = pluginGatewayErrorCode;
+        this.pluginFirstReferenceId = pluginFirstReferenceId;
+        this.pluginSecondReferenceId = pluginSecondReferenceId;
+        this.pluginPmId = pluginPmId;
+        this.pluginPmIsDefault = pluginPmIsDefault;
+        this.pluginPmType = pluginPmType;
+        this.pluginPmCcName = pluginPmCcName;
+        this.pluginPmCcType = pluginPmCcType;
+        this.pluginPmCcExpirationMonth = pluginPmCcExpirationMonth;
+        this.pluginPmCcExpirationYear = pluginPmCcExpirationYear;
+        this.pluginPmCcLast4 = pluginPmCcLast4;
+        this.pluginPmAddress1 = pluginPmAddress1;
+        this.pluginPmAddress2 = pluginPmAddress2;
+        this.pluginPmCity = pluginPmCity;
+        this.pluginPmState = pluginPmState;
+        this.pluginPmZip = pluginPmZip;
+        this.pluginPmCountry = pluginPmCountry;
+    }
+
+    protected BusinessInvoicePaymentBaseModelDao(final Account account,
+                                                 final Long accountRecordId,
+                                                 final Invoice invoice,
+                                                 final InvoicePayment invoicePayment,
+                                                 final Long invoicePaymentRecordId,
+                                                 final Payment payment,
+                                                 @Nullable final Refund refund,
+                                                 final PaymentMethod paymentMethod,
+                                                 final AuditLog creationAuditLog,
+                                                 final Long tenantRecordId) {
+        this(invoicePaymentRecordId,
+             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(),
+             invoice.getOriginalChargedAmount(),
+             invoice.getCreditAdjAmount(),
+             invoicePayment.getType().toString(),
+             payment.getPaymentNumber() == null ? null : payment.getPaymentNumber().longValue(),
+             invoicePayment.getLinkedInvoicePaymentId(),
+             invoicePayment.getAmount(),
+             invoicePayment.getCurrency() == null ? null : invoicePayment.getCurrency().toString(),
+             refund != null ? (refund.getPluginDetail() != null ? refund.getPluginDetail().getCreatedDate() : null) : (payment.getPaymentInfoPlugin() != null ? payment.getPaymentInfoPlugin().getCreatedDate() : null),
+             refund != null ? (refund.getPluginDetail() != null ? refund.getPluginDetail().getEffectiveDate() : null) : (payment.getPaymentInfoPlugin() != null ? payment.getPaymentInfoPlugin().getEffectiveDate() : null),
+             refund != null ? (refund.getPluginDetail() != null ? refund.getPluginDetail().getStatus().toString() : null) : (payment.getPaymentInfoPlugin() != null ? payment.getPaymentInfoPlugin().getStatus().toString() : null),
+             refund != null ? (refund.getPluginDetail() != null ? refund.getPluginDetail().getGatewayError() : null) : (payment.getPaymentInfoPlugin() != null ? payment.getPaymentInfoPlugin().getGatewayError() : null),
+             refund != null ? (refund.getPluginDetail() != null ? refund.getPluginDetail().getGatewayErrorCode() : null) : (payment.getPaymentInfoPlugin() != null ? payment.getPaymentInfoPlugin().getGatewayErrorCode() : null),
+             refund != null ? (refund.getPluginDetail() != null ? refund.getPluginDetail().getReferenceId() : null) : (payment.getPaymentInfoPlugin() != null ? payment.getPaymentInfoPlugin().getFirstPaymentReferenceId() : null),
+             refund != null ? null : (payment.getPaymentInfoPlugin() != null ? payment.getPaymentInfoPlugin().getSecondPaymentReferenceId() : null),
+             paymentMethod.getPluginDetail() != null ? paymentMethod.getPluginDetail().getExternalPaymentMethodId() : null,
+             paymentMethod.getPluginDetail() != null ? paymentMethod.getPluginDetail().isDefaultPaymentMethod() : null,
+             paymentMethod.getPluginDetail() != null ? paymentMethod.getPluginDetail().getType() : null,
+             paymentMethod.getPluginDetail() != null ? paymentMethod.getPluginDetail().getCCName() : null,
+             paymentMethod.getPluginDetail() != null ? paymentMethod.getPluginDetail().getCCType() : null,
+             paymentMethod.getPluginDetail() != null ? paymentMethod.getPluginDetail().getCCExprirationMonth() : null,
+             paymentMethod.getPluginDetail() != null ? paymentMethod.getPluginDetail().getCCExprirationYear() : null,
+             paymentMethod.getPluginDetail() != null ? paymentMethod.getPluginDetail().getCCLast4() : null,
+             paymentMethod.getPluginDetail() != null ? paymentMethod.getPluginDetail().getAddress1() : null,
+             paymentMethod.getPluginDetail() != null ? paymentMethod.getPluginDetail().getAddress2() : null,
+             paymentMethod.getPluginDetail() != null ? paymentMethod.getPluginDetail().getCity() : null,
+             paymentMethod.getPluginDetail() != null ? paymentMethod.getPluginDetail().getState() : null,
+             paymentMethod.getPluginDetail() != null ? paymentMethod.getPluginDetail().getZip() : null,
+             paymentMethod.getPluginDetail() != null ? paymentMethod.getPluginDetail().getCountry() : null,
+             invoicePayment.getCreatedDate(),
+             creationAuditLog.getUserName(),
+             creationAuditLog.getReasonCode(),
+             creationAuditLog.getComment(),
+             account.getId(),
+             account.getName(),
+             account.getExternalKey(),
+             accountRecordId,
+             tenantRecordId);
+    }
+
+    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 String getCurrency() {
+        return currency;
+    }
+
+    public DateTime getPluginCreatedDate() {
+        return pluginCreatedDate;
+    }
+
+    public DateTime getPluginEffectiveDate() {
+        return pluginEffectiveDate;
+    }
+
+    public String getPluginStatus() {
+        return pluginStatus;
+    }
+
+    public String getPluginGatewayError() {
+        return pluginGatewayError;
+    }
+
+    public String getPluginGatewayErrorCode() {
+        return pluginGatewayErrorCode;
+    }
+
+    public String getPluginFirstReferenceId() {
+        return pluginFirstReferenceId;
+    }
+
+    public String getPluginSecondReferenceId() {
+        return pluginSecondReferenceId;
+    }
+
+    public String getPluginPmId() {
+        return pluginPmId;
+    }
+
+    public Boolean getPluginPmIsDefault() {
+        return pluginPmIsDefault;
+    }
+
+    public String getPluginPmType() {
+        return pluginPmType;
+    }
+
+    public String getPluginPmCcName() {
+        return pluginPmCcName;
+    }
+
+    public String getPluginPmCcType() {
+        return pluginPmCcType;
+    }
+
+    public String getPluginPmCcExpirationMonth() {
+        return pluginPmCcExpirationMonth;
+    }
+
+    public String getPluginPmCcExpirationYear() {
+        return pluginPmCcExpirationYear;
+    }
+
+    public String getPluginPmCcLast4() {
+        return pluginPmCcLast4;
+    }
+
+    public String getPluginPmAddress1() {
+        return pluginPmAddress1;
+    }
+
+    public String getPluginPmAddress2() {
+        return pluginPmAddress2;
+    }
+
+    public String getPluginPmCity() {
+        return pluginPmCity;
+    }
+
+    public String getPluginPmState() {
+        return pluginPmState;
+    }
+
+    public String getPluginPmZip() {
+        return pluginPmZip;
+    }
+
+    public String getPluginPmCountry() {
+        return pluginPmCountry;
+    }
+
+    @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).append('\'');
+        sb.append(", pluginCreatedDate=").append(pluginCreatedDate);
+        sb.append(", pluginEffectiveDate=").append(pluginEffectiveDate);
+        sb.append(", pluginStatus='").append(pluginStatus).append('\'');
+        sb.append(", pluginGatewayError='").append(pluginGatewayError).append('\'');
+        sb.append(", pluginGatewayErrorCode='").append(pluginGatewayErrorCode).append('\'');
+        sb.append(", pluginFirstReferenceId='").append(pluginFirstReferenceId).append('\'');
+        sb.append(", pluginSecondReferenceId='").append(pluginSecondReferenceId).append('\'');
+        sb.append(", pluginPmId='").append(pluginPmId).append('\'');
+        sb.append(", pluginPmIsDefault=").append(pluginPmIsDefault);
+        sb.append(", pluginPmType='").append(pluginPmType).append('\'');
+        sb.append(", pluginPmCcName='").append(pluginPmCcName).append('\'');
+        sb.append(", pluginPmCcType='").append(pluginPmCcType).append('\'');
+        sb.append(", pluginPmCcExpirationMonth='").append(pluginPmCcExpirationMonth).append('\'');
+        sb.append(", pluginPmCcExpirationYear='").append(pluginPmCcExpirationYear).append('\'');
+        sb.append(", pluginPmCcLast4='").append(pluginPmCcLast4).append('\'');
+        sb.append(", pluginPmAddress1='").append(pluginPmAddress1).append('\'');
+        sb.append(", pluginPmAddress2='").append(pluginPmAddress2).append('\'');
+        sb.append(", pluginPmCity='").append(pluginPmCity).append('\'');
+        sb.append(", pluginPmState='").append(pluginPmState).append('\'');
+        sb.append(", pluginPmZip='").append(pluginPmZip).append('\'');
+        sb.append(", pluginPmCountry='").append(pluginPmCountry).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 BusinessInvoicePaymentBaseModelDao that = (BusinessInvoicePaymentBaseModelDao) o;
+
+        if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
+            return false;
+        }
+        if (currency != null ? !currency.equals(that.currency) : that.currency != 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 (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;
+        }
+        if (pluginCreatedDate != null ? !pluginCreatedDate.equals(that.pluginCreatedDate) : that.pluginCreatedDate != null) {
+            return false;
+        }
+        if (pluginEffectiveDate != null ? !pluginEffectiveDate.equals(that.pluginEffectiveDate) : that.pluginEffectiveDate != null) {
+            return false;
+        }
+        if (pluginFirstReferenceId != null ? !pluginFirstReferenceId.equals(that.pluginFirstReferenceId) : that.pluginFirstReferenceId != null) {
+            return false;
+        }
+        if (pluginGatewayError != null ? !pluginGatewayError.equals(that.pluginGatewayError) : that.pluginGatewayError != null) {
+            return false;
+        }
+        if (pluginGatewayErrorCode != null ? !pluginGatewayErrorCode.equals(that.pluginGatewayErrorCode) : that.pluginGatewayErrorCode != null) {
+            return false;
+        }
+        if (pluginPmAddress1 != null ? !pluginPmAddress1.equals(that.pluginPmAddress1) : that.pluginPmAddress1 != null) {
+            return false;
+        }
+        if (pluginPmAddress2 != null ? !pluginPmAddress2.equals(that.pluginPmAddress2) : that.pluginPmAddress2 != null) {
+            return false;
+        }
+        if (pluginPmCcExpirationMonth != null ? !pluginPmCcExpirationMonth.equals(that.pluginPmCcExpirationMonth) : that.pluginPmCcExpirationMonth != null) {
+            return false;
+        }
+        if (pluginPmCcExpirationYear != null ? !pluginPmCcExpirationYear.equals(that.pluginPmCcExpirationYear) : that.pluginPmCcExpirationYear != null) {
+            return false;
+        }
+        if (pluginPmCcLast4 != null ? !pluginPmCcLast4.equals(that.pluginPmCcLast4) : that.pluginPmCcLast4 != null) {
+            return false;
+        }
+        if (pluginPmCcName != null ? !pluginPmCcName.equals(that.pluginPmCcName) : that.pluginPmCcName != null) {
+            return false;
+        }
+        if (pluginPmCcType != null ? !pluginPmCcType.equals(that.pluginPmCcType) : that.pluginPmCcType != null) {
+            return false;
+        }
+        if (pluginPmCity != null ? !pluginPmCity.equals(that.pluginPmCity) : that.pluginPmCity != null) {
+            return false;
+        }
+        if (pluginPmCountry != null ? !pluginPmCountry.equals(that.pluginPmCountry) : that.pluginPmCountry != null) {
+            return false;
+        }
+        if (pluginPmId != null ? !pluginPmId.equals(that.pluginPmId) : that.pluginPmId != null) {
+            return false;
+        }
+        if (pluginPmIsDefault != null ? !pluginPmIsDefault.equals(that.pluginPmIsDefault) : that.pluginPmIsDefault != null) {
+            return false;
+        }
+        if (pluginPmState != null ? !pluginPmState.equals(that.pluginPmState) : that.pluginPmState != null) {
+            return false;
+        }
+        if (pluginPmType != null ? !pluginPmType.equals(that.pluginPmType) : that.pluginPmType != null) {
+            return false;
+        }
+        if (pluginPmZip != null ? !pluginPmZip.equals(that.pluginPmZip) : that.pluginPmZip != null) {
+            return false;
+        }
+        if (pluginSecondReferenceId != null ? !pluginSecondReferenceId.equals(that.pluginSecondReferenceId) : that.pluginSecondReferenceId != null) {
+            return false;
+        }
+        if (pluginStatus != null ? !pluginStatus.equals(that.pluginStatus) : that.pluginStatus != 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);
+        result = 31 * result + (pluginCreatedDate != null ? pluginCreatedDate.hashCode() : 0);
+        result = 31 * result + (pluginEffectiveDate != null ? pluginEffectiveDate.hashCode() : 0);
+        result = 31 * result + (pluginStatus != null ? pluginStatus.hashCode() : 0);
+        result = 31 * result + (pluginGatewayError != null ? pluginGatewayError.hashCode() : 0);
+        result = 31 * result + (pluginGatewayErrorCode != null ? pluginGatewayErrorCode.hashCode() : 0);
+        result = 31 * result + (pluginFirstReferenceId != null ? pluginFirstReferenceId.hashCode() : 0);
+        result = 31 * result + (pluginSecondReferenceId != null ? pluginSecondReferenceId.hashCode() : 0);
+        result = 31 * result + (pluginPmId != null ? pluginPmId.hashCode() : 0);
+        result = 31 * result + (pluginPmIsDefault != null ? pluginPmIsDefault.hashCode() : 0);
+        result = 31 * result + (pluginPmType != null ? pluginPmType.hashCode() : 0);
+        result = 31 * result + (pluginPmCcName != null ? pluginPmCcName.hashCode() : 0);
+        result = 31 * result + (pluginPmCcType != null ? pluginPmCcType.hashCode() : 0);
+        result = 31 * result + (pluginPmCcExpirationMonth != null ? pluginPmCcExpirationMonth.hashCode() : 0);
+        result = 31 * result + (pluginPmCcExpirationYear != null ? pluginPmCcExpirationYear.hashCode() : 0);
+        result = 31 * result + (pluginPmCcLast4 != null ? pluginPmCcLast4.hashCode() : 0);
+        result = 31 * result + (pluginPmAddress1 != null ? pluginPmAddress1.hashCode() : 0);
+        result = 31 * result + (pluginPmAddress2 != null ? pluginPmAddress2.hashCode() : 0);
+        result = 31 * result + (pluginPmCity != null ? pluginPmCity.hashCode() : 0);
+        result = 31 * result + (pluginPmState != null ? pluginPmState.hashCode() : 0);
+        result = 31 * result + (pluginPmZip != null ? pluginPmZip.hashCode() : 0);
+        result = 31 * result + (pluginPmCountry != null ? pluginPmCountry.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoicePaymentChargebackModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoicePaymentChargebackModelDao.java
new file mode 100644
index 0000000..3dd149a
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoicePaymentChargebackModelDao.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.osgi.bundles.analytics.dao.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.payment.api.Refund;
+import com.ning.billing.util.audit.AuditLog;
+
+public class BusinessInvoicePaymentChargebackModelDao extends BusinessInvoicePaymentBaseModelDao {
+
+    public BusinessInvoicePaymentChargebackModelDao() { /* When reading from the database */ }
+
+    public BusinessInvoicePaymentChargebackModelDao(final Account account,
+                                                    final Long accountRecordId,
+                                                    final Invoice invoice,
+                                                    final InvoicePayment invoicePayment,
+                                                    final Long invoicePaymentRecordId,
+                                                    final Payment payment,
+                                                    final Refund refund,
+                                                    final PaymentMethod paymentMethod,
+                                                    final AuditLog creationAuditLog,
+                                                    final Long tenantRecordId) {
+        super(account,
+              accountRecordId,
+              invoice,
+              invoicePayment,
+              invoicePaymentRecordId,
+              payment,
+              refund,
+              paymentMethod,
+              creationAuditLog,
+              tenantRecordId);
+    }
+
+    @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/dao/model/BusinessInvoicePaymentModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoicePaymentModelDao.java
new file mode 100644
index 0000000..56e87ba
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoicePaymentModelDao.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.osgi.bundles.analytics.dao.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.payment.api.Refund;
+import com.ning.billing.util.audit.AuditLog;
+
+public class BusinessInvoicePaymentModelDao extends BusinessInvoicePaymentBaseModelDao {
+
+    public BusinessInvoicePaymentModelDao() { /* When reading from the database */ }
+
+    public BusinessInvoicePaymentModelDao(final Account account,
+                                          final Long accountRecordId,
+                                          final Invoice invoice,
+                                          final InvoicePayment invoicePayment,
+                                          final Long invoicePaymentRecordId,
+                                          final Payment payment,
+                                          final Refund refund,
+                                          final PaymentMethod paymentMethod,
+                                          final AuditLog creationAuditLog,
+                                          final Long tenantRecordId) {
+        super(account,
+              accountRecordId,
+              invoice,
+              invoicePayment,
+              invoicePaymentRecordId,
+              payment,
+              refund,
+              paymentMethod,
+              creationAuditLog,
+              tenantRecordId);
+    }
+
+    @Override
+    public String getTableName() {
+        return INVOICE_PAYMENTS_TABLE_NAME;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoicePaymentRefundModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoicePaymentRefundModelDao.java
new file mode 100644
index 0000000..2596e80
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessInvoicePaymentRefundModelDao.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.osgi.bundles.analytics.dao.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.payment.api.Refund;
+import com.ning.billing.util.audit.AuditLog;
+
+public class BusinessInvoicePaymentRefundModelDao extends BusinessInvoicePaymentBaseModelDao {
+
+    public BusinessInvoicePaymentRefundModelDao() { /* When reading from the database */ }
+
+    public BusinessInvoicePaymentRefundModelDao(final Account account,
+                                                final Long accountRecordId,
+                                                final Invoice invoice,
+                                                final InvoicePayment invoicePayment,
+                                                final Long invoicePaymentRecordId,
+                                                final Payment payment,
+                                                final Refund refund,
+                                                final PaymentMethod paymentMethod,
+                                                final AuditLog creationAuditLog,
+                                                final Long tenantRecordId) {
+        super(account,
+              accountRecordId,
+              invoice,
+              invoicePayment,
+              invoicePaymentRecordId,
+              payment,
+              refund,
+              paymentMethod,
+              creationAuditLog,
+              tenantRecordId);
+    }
+
+    @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/dao/model/BusinessModelDaoBase.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessModelDaoBase.java
new file mode 100644
index 0000000..b5afefd
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessModelDaoBase.java
@@ -0,0 +1,168 @@
+/*
+ * 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.dao.model;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+public abstract class BusinessModelDaoBase {
+
+    protected Long recordId;
+
+    protected DateTime createdDate;
+    protected String createdBy;
+    protected String createdReasonCode;
+    protected String createdComments;
+    protected UUID accountId;
+    protected String accountName;
+    protected String accountExternalKey;
+    protected Long accountRecordId;
+    protected Long tenantRecordId;
+
+    public BusinessModelDaoBase() { /* When reading from the database */ }
+
+    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/dao/model/BusinessSubscriptionTransitionModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessSubscriptionTransitionModelDao.java
new file mode 100644
index 0000000..351c879
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessSubscriptionTransitionModelDao.java
@@ -0,0 +1,519 @@
+/*
+ * 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.dao.model;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.util.audit.AuditLog;
+
+/**
+ * Describe a state change between two BusinessSubscription
+ */
+public class BusinessSubscriptionTransitionModelDao extends BusinessModelDaoBase {
+
+    private static final String SUBSCRIPTION_TABLE_NAME = "bst";
+
+    private Long subscriptionEventRecordId;
+    private UUID bundleId;
+    private String bundleExternalKey;
+    private UUID subscriptionId;
+    private DateTime requestedTimestamp;
+    private String event;
+
+    private String prevProductName;
+    private String prevProductType;
+    private String prevProductCategory;
+    private String prevSlug;
+    private String prevPhase;
+    private String prevBillingPeriod;
+    private BigDecimal prevPrice;
+    private String prevPriceList;
+    private BigDecimal prevMrr;
+    private String prevCurrency;
+    private Boolean prevBusinessActive;
+    private DateTime prevStartDate;
+    private String prevState;
+
+    private String nextProductName;
+    private String nextProductType;
+    private String nextProductCategory;
+    private String nextSlug;
+    private String nextPhase;
+    private String nextBillingPeriod;
+    private BigDecimal nextPrice;
+    private String nextPriceList;
+    private BigDecimal nextMrr;
+    private String nextCurrency;
+    private Boolean nextBusinessActive;
+    private DateTime nextStartDate;
+    private DateTime nextEndDate;
+    private String nextState;
+
+    public BusinessSubscriptionTransitionModelDao() { /* When reading from the database */ }
+
+    public BusinessSubscriptionTransitionModelDao(final Long subscriptionEventRecordId,
+                                                  final UUID bundleId,
+                                                  final String bundleExternalKey,
+                                                  final UUID subscriptionId,
+                                                  final DateTime requestedTimestamp,
+                                                  final BusinessSubscriptionEvent event,
+                                                  @Nullable 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.bundleId = bundleId;
+        this.bundleExternalKey = bundleExternalKey;
+        this.subscriptionId = subscriptionId;
+
+        this.requestedTimestamp = requestedTimestamp;
+        this.event = event.toString();
+
+        if (previousSubscription != null) {
+            this.prevProductName = previousSubscription.getProductName();
+            this.prevProductType = previousSubscription.getProductType();
+            this.prevProductCategory = previousSubscription.getProductCategory();
+            this.prevSlug = previousSubscription.getSlug();
+            this.prevPhase = previousSubscription.getPhase();
+            this.prevBillingPeriod = previousSubscription.getBillingPeriod();
+            this.prevPrice = previousSubscription.getPrice();
+            this.prevPriceList = previousSubscription.getPriceList();
+            this.prevMrr = previousSubscription.getMrr();
+            this.prevCurrency = previousSubscription.getCurrency();
+            this.prevBusinessActive = previousSubscription.getBusinessActive();
+            this.prevStartDate = previousSubscription.getStartDate();
+            this.prevState = previousSubscription.getState();
+        } else {
+            this.prevProductName = null;
+            this.prevProductType = null;
+            this.prevProductCategory = null;
+            this.prevSlug = null;
+            this.prevPhase = null;
+            this.prevBillingPeriod = null;
+            this.prevPrice = null;
+            this.prevPriceList = null;
+            this.prevMrr = null;
+            this.prevCurrency = null;
+            this.prevBusinessActive = null;
+            this.prevStartDate = null;
+            this.prevState = null;
+        }
+
+        this.nextProductName = nextSubscription.getProductName();
+        this.nextProductType = nextSubscription.getProductType();
+        this.nextProductCategory = nextSubscription.getProductCategory();
+        this.nextSlug = nextSubscription.getSlug();
+        this.nextPhase = nextSubscription.getPhase();
+        this.nextBillingPeriod = nextSubscription.getBillingPeriod();
+        this.nextPrice = nextSubscription.getPrice();
+        this.nextPriceList = nextSubscription.getPriceList();
+        this.nextMrr = nextSubscription.getMrr();
+        this.nextCurrency = nextSubscription.getCurrency();
+        this.nextBusinessActive = nextSubscription.getBusinessActive();
+        this.nextStartDate = nextSubscription.getStartDate();
+        this.nextEndDate = nextSubscription.getEndDate();
+        this.nextState = nextSubscription.getState();
+    }
+
+    public BusinessSubscriptionTransitionModelDao(final Account account,
+                                                  final Long accountRecordId,
+                                                  final SubscriptionBundle bundle,
+                                                  final SubscriptionTransition transition,
+                                                  final Long subscriptionEventRecordId,
+                                                  final DateTime requestedTimestamp,
+                                                  final BusinessSubscriptionEvent event,
+                                                  @Nullable final BusinessSubscription previousSubscription,
+                                                  final BusinessSubscription nextSubscription,
+                                                  final AuditLog creationAuditLog,
+                                                  final Long tenantRecordId) {
+        this(subscriptionEventRecordId,
+             bundle.getId(),
+             bundle.getExternalKey(),
+             transition.getSubscriptionId(),
+             requestedTimestamp,
+             event,
+             previousSubscription,
+             nextSubscription,
+             transition.getNextEventCreatedDate(),
+             creationAuditLog.getUserName(),
+             creationAuditLog.getReasonCode(),
+             creationAuditLog.getComment(),
+             account.getId(),
+             account.getName(),
+             account.getExternalKey(),
+             accountRecordId,
+             tenantRecordId);
+    }
+
+    @Override
+    public String getTableName() {
+        return SUBSCRIPTION_TABLE_NAME;
+    }
+
+    public Long getSubscriptionEventRecordId() {
+        return subscriptionEventRecordId;
+    }
+
+    public UUID getBundleId() {
+        return bundleId;
+    }
+
+    public String getBundleExternalKey() {
+        return bundleExternalKey;
+    }
+
+    public UUID getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    public DateTime getRequestedTimestamp() {
+        return requestedTimestamp;
+    }
+
+    public String getEvent() {
+        return event;
+    }
+
+    public String getPrevProductName() {
+        return prevProductName;
+    }
+
+    public String getPrevProductType() {
+        return prevProductType;
+    }
+
+    public String getPrevProductCategory() {
+        return prevProductCategory;
+    }
+
+    public String getPrevSlug() {
+        return prevSlug;
+    }
+
+    public String getPrevPhase() {
+        return prevPhase;
+    }
+
+    public String getPrevBillingPeriod() {
+        return prevBillingPeriod;
+    }
+
+    public BigDecimal getPrevPrice() {
+        return prevPrice;
+    }
+
+    public String getPrevPriceList() {
+        return prevPriceList;
+    }
+
+    public BigDecimal getPrevMrr() {
+        return prevMrr;
+    }
+
+    public String getPrevCurrency() {
+        return prevCurrency;
+    }
+
+    public Boolean getPrevBusinessActive() {
+        return prevBusinessActive;
+    }
+
+    public DateTime getPrevStartDate() {
+        return prevStartDate;
+    }
+
+    public String getPrevState() {
+        return prevState;
+    }
+
+    public String getNextProductName() {
+        return nextProductName;
+    }
+
+    public String getNextProductType() {
+        return nextProductType;
+    }
+
+    public String getNextProductCategory() {
+        return nextProductCategory;
+    }
+
+    public String getNextSlug() {
+        return nextSlug;
+    }
+
+    public String getNextPhase() {
+        return nextPhase;
+    }
+
+    public String getNextBillingPeriod() {
+        return nextBillingPeriod;
+    }
+
+    public BigDecimal getNextPrice() {
+        return nextPrice;
+    }
+
+    public String getNextPriceList() {
+        return nextPriceList;
+    }
+
+    public BigDecimal getNextMrr() {
+        return nextMrr;
+    }
+
+    public String getNextCurrency() {
+        return nextCurrency;
+    }
+
+    public Boolean getNextBusinessActive() {
+        return nextBusinessActive;
+    }
+
+    public DateTime getNextStartDate() {
+        return nextStartDate;
+    }
+
+    public DateTime getNextEndDate() {
+        return nextEndDate;
+    }
+
+    public String getNextState() {
+        return nextState;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessSubscriptionTransitionModelDao");
+        sb.append("{subscriptionEventRecordId=").append(subscriptionEventRecordId);
+        sb.append(", bundleId=").append(bundleId);
+        sb.append(", bundleExternalKey='").append(bundleExternalKey).append('\'');
+        sb.append(", subscriptionId=").append(subscriptionId);
+        sb.append(", requestedTimestamp=").append(requestedTimestamp);
+        sb.append(", event='").append(event).append('\'');
+        sb.append(", prevProductName='").append(prevProductName).append('\'');
+        sb.append(", prevProductType='").append(prevProductType).append('\'');
+        sb.append(", prevProductCategory='").append(prevProductCategory).append('\'');
+        sb.append(", prevSlug='").append(prevSlug).append('\'');
+        sb.append(", prevPhase='").append(prevPhase).append('\'');
+        sb.append(", prevBillingPeriod='").append(prevBillingPeriod).append('\'');
+        sb.append(", prevPrice=").append(prevPrice);
+        sb.append(", prevPriceList='").append(prevPriceList).append('\'');
+        sb.append(", prevMrr=").append(prevMrr);
+        sb.append(", prevCurrency='").append(prevCurrency).append('\'');
+        sb.append(", prevBusinessActive=").append(prevBusinessActive);
+        sb.append(", prevStartDate=").append(prevStartDate);
+        sb.append(", prevState='").append(prevState).append('\'');
+        sb.append(", nextProductName='").append(nextProductName).append('\'');
+        sb.append(", nextProductType='").append(nextProductType).append('\'');
+        sb.append(", nextProductCategory='").append(nextProductCategory).append('\'');
+        sb.append(", nextSlug='").append(nextSlug).append('\'');
+        sb.append(", nextPhase='").append(nextPhase).append('\'');
+        sb.append(", nextBillingPeriod='").append(nextBillingPeriod).append('\'');
+        sb.append(", nextPrice=").append(nextPrice);
+        sb.append(", nextPriceList='").append(nextPriceList).append('\'');
+        sb.append(", nextMrr=").append(nextMrr);
+        sb.append(", nextCurrency='").append(nextCurrency).append('\'');
+        sb.append(", nextBusinessActive=").append(nextBusinessActive);
+        sb.append(", nextStartDate=").append(nextStartDate);
+        sb.append(", nextEndDate=").append(nextEndDate);
+        sb.append(", nextState='").append(nextState).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 BusinessSubscriptionTransitionModelDao that = (BusinessSubscriptionTransitionModelDao) o;
+
+        if (bundleExternalKey != null ? !bundleExternalKey.equals(that.bundleExternalKey) : that.bundleExternalKey != null) {
+            return false;
+        }
+        if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
+            return false;
+        }
+        if (event != null ? !event.equals(that.event) : that.event != null) {
+            return false;
+        }
+        if (nextBillingPeriod != null ? !nextBillingPeriod.equals(that.nextBillingPeriod) : that.nextBillingPeriod != null) {
+            return false;
+        }
+        if (nextBusinessActive != null ? !nextBusinessActive.equals(that.nextBusinessActive) : that.nextBusinessActive != null) {
+            return false;
+        }
+        if (nextCurrency != null ? !nextCurrency.equals(that.nextCurrency) : that.nextCurrency != null) {
+            return false;
+        }
+        if (nextEndDate != null ? !nextEndDate.equals(that.nextEndDate) : that.nextEndDate != null) {
+            return false;
+        }
+        if (nextMrr != null ? !nextMrr.equals(that.nextMrr) : that.nextMrr != null) {
+            return false;
+        }
+        if (nextPhase != null ? !nextPhase.equals(that.nextPhase) : that.nextPhase != null) {
+            return false;
+        }
+        if (nextPrice != null ? !nextPrice.equals(that.nextPrice) : that.nextPrice != null) {
+            return false;
+        }
+        if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
+            return false;
+        }
+        if (nextProductCategory != null ? !nextProductCategory.equals(that.nextProductCategory) : that.nextProductCategory != null) {
+            return false;
+        }
+        if (nextProductName != null ? !nextProductName.equals(that.nextProductName) : that.nextProductName != null) {
+            return false;
+        }
+        if (nextProductType != null ? !nextProductType.equals(that.nextProductType) : that.nextProductType != null) {
+            return false;
+        }
+        if (nextSlug != null ? !nextSlug.equals(that.nextSlug) : that.nextSlug != null) {
+            return false;
+        }
+        if (nextStartDate != null ? !nextStartDate.equals(that.nextStartDate) : that.nextStartDate != null) {
+            return false;
+        }
+        if (nextState != null ? !nextState.equals(that.nextState) : that.nextState != null) {
+            return false;
+        }
+        if (prevBillingPeriod != null ? !prevBillingPeriod.equals(that.prevBillingPeriod) : that.prevBillingPeriod != null) {
+            return false;
+        }
+        if (prevBusinessActive != null ? !prevBusinessActive.equals(that.prevBusinessActive) : that.prevBusinessActive != null) {
+            return false;
+        }
+        if (prevCurrency != null ? !prevCurrency.equals(that.prevCurrency) : that.prevCurrency != null) {
+            return false;
+        }
+        if (prevMrr != null ? !prevMrr.equals(that.prevMrr) : that.prevMrr != null) {
+            return false;
+        }
+        if (prevPhase != null ? !prevPhase.equals(that.prevPhase) : that.prevPhase != null) {
+            return false;
+        }
+        if (prevPrice != null ? !prevPrice.equals(that.prevPrice) : that.prevPrice != null) {
+            return false;
+        }
+        if (prevPriceList != null ? !prevPriceList.equals(that.prevPriceList) : that.prevPriceList != null) {
+            return false;
+        }
+        if (prevProductCategory != null ? !prevProductCategory.equals(that.prevProductCategory) : that.prevProductCategory != null) {
+            return false;
+        }
+        if (prevProductName != null ? !prevProductName.equals(that.prevProductName) : that.prevProductName != null) {
+            return false;
+        }
+        if (prevProductType != null ? !prevProductType.equals(that.prevProductType) : that.prevProductType != null) {
+            return false;
+        }
+        if (prevSlug != null ? !prevSlug.equals(that.prevSlug) : that.prevSlug != null) {
+            return false;
+        }
+        if (prevStartDate != null ? !prevStartDate.equals(that.prevStartDate) : that.prevStartDate != null) {
+            return false;
+        }
+        if (prevState != null ? !prevState.equals(that.prevState) : that.prevState != null) {
+            return false;
+        }
+        if (requestedTimestamp != null ? !requestedTimestamp.equals(that.requestedTimestamp) : that.requestedTimestamp != null) {
+            return false;
+        }
+        if (subscriptionEventRecordId != null ? !subscriptionEventRecordId.equals(that.subscriptionEventRecordId) : that.subscriptionEventRecordId != null) {
+            return false;
+        }
+        if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (subscriptionEventRecordId != null ? subscriptionEventRecordId.hashCode() : 0);
+        result = 31 * result + (bundleId != null ? bundleId.hashCode() : 0);
+        result = 31 * result + (bundleExternalKey != null ? bundleExternalKey.hashCode() : 0);
+        result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
+        result = 31 * result + (requestedTimestamp != null ? requestedTimestamp.hashCode() : 0);
+        result = 31 * result + (event != null ? event.hashCode() : 0);
+        result = 31 * result + (prevProductName != null ? prevProductName.hashCode() : 0);
+        result = 31 * result + (prevProductType != null ? prevProductType.hashCode() : 0);
+        result = 31 * result + (prevProductCategory != null ? prevProductCategory.hashCode() : 0);
+        result = 31 * result + (prevSlug != null ? prevSlug.hashCode() : 0);
+        result = 31 * result + (prevPhase != null ? prevPhase.hashCode() : 0);
+        result = 31 * result + (prevBillingPeriod != null ? prevBillingPeriod.hashCode() : 0);
+        result = 31 * result + (prevPrice != null ? prevPrice.hashCode() : 0);
+        result = 31 * result + (prevPriceList != null ? prevPriceList.hashCode() : 0);
+        result = 31 * result + (prevMrr != null ? prevMrr.hashCode() : 0);
+        result = 31 * result + (prevCurrency != null ? prevCurrency.hashCode() : 0);
+        result = 31 * result + (prevBusinessActive != null ? prevBusinessActive.hashCode() : 0);
+        result = 31 * result + (prevStartDate != null ? prevStartDate.hashCode() : 0);
+        result = 31 * result + (prevState != null ? prevState.hashCode() : 0);
+        result = 31 * result + (nextProductName != null ? nextProductName.hashCode() : 0);
+        result = 31 * result + (nextProductType != null ? nextProductType.hashCode() : 0);
+        result = 31 * result + (nextProductCategory != null ? nextProductCategory.hashCode() : 0);
+        result = 31 * result + (nextSlug != null ? nextSlug.hashCode() : 0);
+        result = 31 * result + (nextPhase != null ? nextPhase.hashCode() : 0);
+        result = 31 * result + (nextBillingPeriod != null ? nextBillingPeriod.hashCode() : 0);
+        result = 31 * result + (nextPrice != null ? nextPrice.hashCode() : 0);
+        result = 31 * result + (nextPriceList != null ? nextPriceList.hashCode() : 0);
+        result = 31 * result + (nextMrr != null ? nextMrr.hashCode() : 0);
+        result = 31 * result + (nextCurrency != null ? nextCurrency.hashCode() : 0);
+        result = 31 * result + (nextBusinessActive != null ? nextBusinessActive.hashCode() : 0);
+        result = 31 * result + (nextStartDate != null ? nextStartDate.hashCode() : 0);
+        result = 31 * result + (nextEndDate != null ? nextEndDate.hashCode() : 0);
+        result = 31 * result + (nextState != null ? nextState.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessTagModelDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessTagModelDao.java
new file mode 100644
index 0000000..b3b767d
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/model/BusinessTagModelDao.java
@@ -0,0 +1,172 @@
+/*
+ * 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.dao.model;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
+
+public abstract class BusinessTagModelDao extends BusinessModelDaoBase {
+
+    protected static final String ACCOUNT_TAGS_TABLE_NAME = "bac_tags";
+    protected static final String INVOICE_PAYMENT_TAGS_TABLE_NAME = "bip_tags";
+    protected static final String INVOICE_TAGS_TABLE_NAME = "bin_tags";
+
+    public static final String[] ALL_TAGS_TABLE_NAMES = new String[]{ACCOUNT_TAGS_TABLE_NAME, INVOICE_PAYMENT_TAGS_TABLE_NAME, INVOICE_TAGS_TABLE_NAME};
+
+    private Long tagRecordId;
+    private String name;
+
+    public static BusinessTagModelDao create(final Account account,
+                                             final Long accountRecordId,
+                                             final Tag tag,
+                                             final Long tagRecordId,
+                                             final TagDefinition tagDefinition,
+                                             final AuditLog creationAuditLog,
+                                             final Long tenantRecordId) {
+        if (ObjectType.ACCOUNT.equals(tag.getObjectType())) {
+            return new BusinessAccountTagModelDao(account,
+                                                  accountRecordId,
+                                                  tag,
+                                                  tagRecordId,
+                                                  tagDefinition,
+                                                  creationAuditLog,
+                                                  tenantRecordId);
+        } else if (ObjectType.INVOICE_PAYMENT.equals(tag.getObjectType())) {
+            return new BusinessInvoicePaymentTagModelDao(account,
+                                                         accountRecordId,
+                                                         tag,
+                                                         tagRecordId,
+                                                         tagDefinition,
+                                                         creationAuditLog,
+                                                         tenantRecordId);
+        } else if (ObjectType.INVOICE.equals(tag.getObjectType())) {
+            return new BusinessInvoiceTagModelDao(account,
+                                                  accountRecordId,
+                                                  tag,
+                                                  tagRecordId,
+                                                  tagDefinition,
+                                                  creationAuditLog,
+                                                  tenantRecordId);
+        } else {
+            // We don't care
+            return null;
+        }
+    }
+
+    public BusinessTagModelDao() { /* When reading from the database */ }
+
+    public BusinessTagModelDao(final Long tagRecordId,
+                               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.tagRecordId = tagRecordId;
+        this.name = name;
+    }
+
+    public BusinessTagModelDao(final Account account,
+                               final Long accountRecordId,
+                               final Tag tag,
+                               final Long tagRecordId,
+                               final TagDefinition tagDefinition,
+                               final AuditLog creationAuditLog,
+                               final Long tenantRecordId) {
+        this(tagRecordId,
+             tagDefinition.getName(),
+             tag.getCreatedDate(),
+             creationAuditLog.getUserName(),
+             creationAuditLog.getReasonCode(),
+             creationAuditLog.getComment(),
+             account.getId(),
+             account.getName(),
+             account.getExternalKey(),
+             accountRecordId,
+             tenantRecordId);
+    }
+
+    public Long getTagRecordId() {
+        return tagRecordId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessTagModelDao");
+        sb.append("{tagRecordId=").append(tagRecordId);
+        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;
+        }
+        if (tagRecordId != null ? !tagRecordId.equals(that.tagRecordId) : that.tagRecordId != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (tagRecordId != null ? tagRecordId.hashCode() : 0);
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/http/AnalyticsServlet.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/http/AnalyticsServlet.java
new file mode 100644
index 0000000..c025b8b
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/http/AnalyticsServlet.java
@@ -0,0 +1,185 @@
+/*
+ * 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.http;
+
+import java.io.IOException;
+import java.util.UUID;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import com.ning.billing.osgi.bundles.analytics.AnalyticsRefreshException;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessSnapshot;
+import com.ning.billing.osgi.bundles.analytics.api.user.AnalyticsUserApi;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Objects;
+
+public class AnalyticsServlet extends HttpServlet {
+
+    private static final String QUERY_TENANT_ID = "tenantId";
+    private static final String HDR_CREATED_BY = "X-Killbill-CreatedBy";
+    private static final String HDR_REASON = "X-Killbill-Reason";
+    private static final String HDR_COMMENT = "X-Killbill-Comment";
+
+    private static final ObjectMapper mapper = ObjectMapperProvider.get();
+
+    private final AnalyticsUserApi analyticsUserApi;
+
+    public AnalyticsServlet(final AnalyticsUserApi analyticsUserApi) {
+        this.analyticsUserApi = analyticsUserApi;
+    }
+
+    @Override
+    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        final UUID kbAccountId = getKbAccountId(req, resp);
+        final CallContext context = createCallContext(req, resp);
+
+        final BusinessSnapshot businessSnapshot = analyticsUserApi.getBusinessSnapshot(kbAccountId, context);
+        resp.getOutputStream().write(mapper.writeValueAsBytes(businessSnapshot));
+        resp.setStatus(HttpServletResponse.SC_OK);
+    }
+
+    @Override
+    protected void doPut(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        final UUID kbAccountId = getKbAccountId(req, resp);
+        final CallContext context = createCallContext(req, resp);
+
+        try {
+            analyticsUserApi.rebuildAnalyticsForAccount(kbAccountId, context);
+            resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
+        } catch (AnalyticsRefreshException e) {
+            resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
+        }
+    }
+
+    private CallContext createCallContext(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
+        final String createdBy = Objects.firstNonNull(req.getHeader(HDR_CREATED_BY), req.getRemoteAddr());
+        final String reason = req.getHeader(HDR_REASON);
+        final String comment = Objects.firstNonNull(req.getHeader(HDR_COMMENT), req.getRequestURI());
+
+        final String tenantIdString = req.getParameter(QUERY_TENANT_ID);
+        if (tenantIdString == null) {
+            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing tenantId query parameter in request: " + req.getPathInfo());
+            return null;
+        }
+
+        final UUID tenantId;
+        try {
+            tenantId = UUID.fromString(tenantIdString);
+        } catch (final IllegalArgumentException e) {
+            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid UUID for tenant id: " + tenantIdString);
+            return null;
+        }
+
+        return new AnalyticsApiCallContext(createdBy, reason, comment, tenantId);
+    }
+
+    private UUID getKbAccountId(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
+        final String kbAccountIdString;
+        try {
+            kbAccountIdString = req.getPathInfo().substring(1, req.getPathInfo().length());
+        } catch (final StringIndexOutOfBoundsException e) {
+            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Badly formed kb account id in request: " + req.getPathInfo());
+            return null;
+        }
+
+        final UUID kbAccountId;
+        try {
+            kbAccountId = UUID.fromString(kbAccountIdString);
+        } catch (final IllegalArgumentException e) {
+            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid UUID for kb account id: " + kbAccountIdString);
+            return null;
+        }
+
+        return kbAccountId;
+    }
+
+    private static final class AnalyticsApiCallContext implements CallContext {
+
+        private final String createdBy;
+        private final String reason;
+        private final String comment;
+        private final UUID tenantId;
+        private final DateTime now;
+
+        private AnalyticsApiCallContext(final String createdBy,
+                                        final String reason,
+                                        final String comment,
+                                        final UUID tenantId) {
+            this.createdBy = createdBy;
+            this.reason = reason;
+            this.comment = comment;
+            this.tenantId = tenantId;
+
+            this.now = new DateTime(DateTimeZone.UTC);
+        }
+
+        @Override
+        public UUID getUserToken() {
+            return UUID.randomUUID();
+        }
+
+        @Override
+        public String getUserName() {
+            return createdBy;
+        }
+
+        @Override
+        public CallOrigin getCallOrigin() {
+            return CallOrigin.EXTERNAL;
+        }
+
+        @Override
+        public UserType getUserType() {
+            return UserType.ADMIN;
+        }
+
+        @Override
+        public String getReasonCode() {
+            return reason;
+        }
+
+        @Override
+        public String getComments() {
+            return comment;
+        }
+
+        @Override
+        public DateTime getCreatedDate() {
+            return now;
+        }
+
+        @Override
+        public DateTime getUpdatedDate() {
+            return now;
+        }
+
+        @Override
+        public UUID getTenantId() {
+            return tenantId;
+        }
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessAnalyticsSqlDao.sql.stg b/osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessAnalyticsSqlDao.sql.stg
new file mode 100644
index 0000000..72e3833
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/resources/com/ning/billing/osgi/bundles/analytics/dao/BusinessAnalyticsSqlDao.sql.stg
@@ -0,0 +1,1008 @@
+group BusinessAnalyticsSqlDao;
+
+createBst() ::= <<
+insert into bst (
+  subscription_event_record_id
+, bundle_id
+, bundle_external_key
+, subscription_id
+, requested_timestamp
+, event
+, prev_product_name
+, prev_product_type
+, prev_product_category
+, prev_slug
+, prev_phase
+, prev_billing_period
+, prev_price
+, prev_price_list
+, prev_mrr
+, prev_currency
+, prev_state
+, prev_business_active
+, prev_start_date
+, next_product_name
+, next_product_type
+, next_product_category
+, next_slug
+, next_phase
+, next_billing_period
+, next_price
+, next_price_list
+, next_mrr
+, next_currency
+, next_state
+, next_business_active
+, next_start_date
+, next_end_date
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :subscriptionEventRecordId
+, :bundleId
+, :bundleExternalKey
+, :subscriptionId
+, :requestedTimestamp
+, :event
+, :prevProductName
+, :prevProductType
+, :prevProductCategory
+, :prevSlug
+, :prevPhase
+, :prevBillingPeriod
+, :prevPrice
+, :prevPriceList
+, :prevMrr
+, :prevCurrency
+, :prevState
+, :prevBusinessActive
+, :prevStartDate
+, :nextProductName
+, :nextProductType
+, :nextProductCategory
+, :nextSlug
+, :nextPhase
+, :nextBillingPeriod
+, :nextPrice
+, :nextPriceList
+, :nextMrr
+, :nextCurrency
+, :nextState
+, :nextBusinessActive
+, :nextStartDate
+, :nextEndDate
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBac() ::= <<
+insert into bac (
+  email
+, first_name_length
+, currency
+, billing_cycle_day_local
+, payment_method_id
+, time_zone
+, locale
+, address1
+, address2
+, company_name
+, city
+, state_or_province
+, country
+, postal_code
+, phone
+, migrated
+, notified_for_invoices
+, balance
+, last_invoice_date
+, last_payment_date
+, last_payment_status
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, updated_date
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :email
+, :firstNameLength
+, :currency
+, :billingCycleDayLocal
+, :paymentMethodId
+, :timeZone
+, :locale
+, :address1
+, :address2
+, :companyName
+, :city
+, :stateOrProvince
+, :country
+, :postalCode
+, :phone
+, :migrated
+, :notifiedForInvoices
+, :balance
+, :lastInvoiceDate
+, :lastPaymentDate
+, :lastPaymentStatus
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :updatedDate
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBin() ::= <<
+insert into bin (
+  invoice_record_id
+, invoice_id
+, invoice_number
+, invoice_date
+, target_date
+, currency
+, balance
+, amount_paid
+, amount_charged
+, original_amount_charged
+, amount_credited
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+) values (
+  :invoiceRecordId
+, :invoiceId
+, :invoiceNumber
+, :invoiceDate
+, :targetDate
+, :currency
+, :balance
+, :amountPaid
+, :amountCharged
+, :originalAmountCharged
+, :amountCredited
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+);
+>>
+
+createBia() ::= <<
+insert into bia (
+, invoice_item_record_id
+, second_invoice_item_record_id
+, item_id
+, invoice_id
+, invoice_number
+, invoice_created_date
+, invoice_date
+, invoice_target_date
+, invoice_currency
+, invoice_balance
+, invoice_amount_paid
+, invoice_amount_charged
+, invoice_original_amount_charged
+, invoice_amount_credited
+, item_type
+, revenue_recognizable
+, bundle_external_key
+, product_name
+, product_type
+, product_category
+, slug
+, phase
+, billing_period
+, start_date
+, end_date
+, amount
+, currency
+, linked_item_id
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :invoiceItemRecordId
+, :secondInvoiceItemRecordId
+, :itemId
+, :invoiceId
+, :invoiceNumber
+, :invoiceCreatedDate
+, :invoiceDate
+, :invoiceTargetDate
+, :invoiceCurrency
+, :invoiceBalance
+, :invoiceAmountPaid
+, :invoiceAmountCharged
+, :invoiceOriginalAmountCharged
+, :invoiceAmountCredited
+, :itemType
+, :revenueRecognizable
+, :bundleExternalKey
+, :productName
+, :productType
+, :productCategory
+, :slug
+, :phase
+, :billingPeriod
+, :startDate
+, :endDate
+, :amount
+, :currency
+, :linkedItemId
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBii() ::= <<
+insert into bii (
+  invoice_item_record_id
+, second_invoice_item_record_id
+, item_id
+, invoice_id
+, invoice_number
+, invoice_created_date
+, invoice_date
+, invoice_target_date
+, invoice_currency
+, invoice_balance
+, invoice_amount_paid
+, invoice_amount_charged
+, invoice_original_amount_charged
+, invoice_amount_credited
+, item_type
+, revenue_recognizable
+, bundle_external_key
+, product_name
+, product_type
+, product_category
+, slug
+, phase
+, billing_period
+, start_date
+, end_date
+, amount
+, currency
+, linked_item_id
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :invoiceItemRecordId
+, :secondInvoiceItemRecordId
+, :itemId
+, :invoiceId
+, :invoiceNumber
+, :invoiceCreatedDate
+, :invoiceDate
+, :invoiceTargetDate
+, :invoiceCurrency
+, :invoiceBalance
+, :invoiceAmountPaid
+, :invoiceAmountCharged
+, :invoiceOriginalAmountCharged
+, :invoiceAmountCredited
+, :itemType
+, :revenueRecognizable
+, :bundleExternalKey
+, :productName
+, :productType
+, :productCategory
+, :slug
+, :phase
+, :billingPeriod
+, :startDate
+, :endDate
+, :amount
+, :currency
+, :linkedItemId
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBiia() ::= <<
+insert into biia (
+  invoice_item_record_id
+, second_invoice_item_record_id
+, item_id
+, invoice_id
+, invoice_number
+, invoice_created_date
+, invoice_date
+, invoice_target_date
+, invoice_currency
+, invoice_balance
+, invoice_amount_paid
+, invoice_amount_charged
+, invoice_original_amount_charged
+, invoice_amount_credited
+, item_type
+, revenue_recognizable
+, bundle_external_key
+, product_name
+, product_type
+, product_category
+, slug
+, phase
+, billing_period
+, start_date
+, end_date
+, amount
+, currency
+, linked_item_id
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :invoiceItemRecordId
+, :secondInvoiceItemRecordId
+, :itemId
+, :invoiceId
+, :invoiceNumber
+, :invoiceCreatedDate
+, :invoiceDate
+, :invoiceTargetDate
+, :invoiceCurrency
+, :invoiceBalance
+, :invoiceAmountPaid
+, :invoiceAmountCharged
+, :invoiceOriginalAmountCharged
+, :invoiceAmountCredited
+, :itemType
+, :revenueRecognizable
+, :bundleExternalKey
+, :productName
+, :productType
+, :productCategory
+, :slug
+, :phase
+, :billingPeriod
+, :startDate
+, :endDate
+, :amount
+, :currency
+, :linkedItemId
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBiic() ::= <<
+insert into biic (
+  invoice_item_record_id
+, second_invoice_item_record_id
+, item_id
+, invoice_id
+, invoice_number
+, invoice_created_date
+, invoice_date
+, invoice_target_date
+, invoice_currency
+, invoice_balance
+, invoice_amount_paid
+, invoice_amount_charged
+, invoice_original_amount_charged
+, invoice_amount_credited
+, item_type
+, revenue_recognizable
+, bundle_external_key
+, product_name
+, product_type
+, product_category
+, slug
+, phase
+, billing_period
+, start_date
+, end_date
+, amount
+, currency
+, linked_item_id
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :invoiceItemRecordId
+, :secondInvoiceItemRecordId
+, :itemId
+, :invoiceId
+, :invoiceNumber
+, :invoiceCreatedDate
+, :invoiceDate
+, :invoiceTargetDate
+, :invoiceCurrency
+, :invoiceBalance
+, :invoiceAmountPaid
+, :invoiceAmountCharged
+, :invoiceOriginalAmountCharged
+, :invoiceAmountCredited
+, :itemType
+, :revenueRecognizable
+, :bundleExternalKey
+, :productName
+, :productType
+, :productCategory
+, :slug
+, :phase
+, :billingPeriod
+, :startDate
+, :endDate
+, :amount
+, :currency
+, :linkedItemId
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBip() ::= <<
+insert into bip (
+  invoice_payment_record_id
+, invoice_payment_id
+, invoice_id
+, invoice_number
+, invoice_created_date
+, invoice_date
+, invoice_target_date
+, invoice_currency
+, invoice_balance
+, invoice_amount_paid
+, invoice_amount_charged
+, invoice_original_amount_charged
+, invoice_amount_credited
+, invoice_payment_type
+, payment_number
+, linked_invoice_payment_id
+, amount
+, currency
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :invoicePaymentRecordId
+, :invoicePaymentId
+, :invoiceId
+, :invoiceNumber
+, :invoiceCreatedDate
+, :invoiceDate
+, :invoiceTargetDate
+, :invoiceCurrency
+, :invoiceBalance
+, :invoiceAmountPaid
+, :invoiceAmountCharged
+, :invoiceOriginalAmountCharged
+, :invoiceAmountCredited
+, :invoicePaymentType
+, :paymentNumber
+, :linkedInvoicePaymentId
+, :amount
+, :currency
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBipr() ::= <<
+insert into bipr (
+  invoice_payment_record_id
+, invoice_payment_id
+, invoice_id
+, invoice_number
+, invoice_created_date
+, invoice_date
+, invoice_target_date
+, invoice_currency
+, invoice_balance
+, invoice_amount_paid
+, invoice_amount_charged
+, invoice_original_amount_charged
+, invoice_amount_credited
+, invoice_payment_type
+, payment_number
+, linked_invoice_payment_id
+, amount
+, currency
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :invoicePaymentRecordId
+, :invoicePaymentId
+, :invoiceId
+, :invoiceNumber
+, :invoiceCreatedDate
+, :invoiceDate
+, :invoiceTargetDate
+, :invoiceCurrency
+, :invoiceBalance
+, :invoiceAmountPaid
+, :invoiceAmountCharged
+, :invoiceOriginalAmountCharged
+, :invoiceAmountCredited
+, :invoicePaymentType
+, :paymentNumber
+, :linkedInvoicePaymentId
+, :amount
+, :currency
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBipc() ::= <<
+insert into bipc (
+  invoice_payment_record_id
+, invoice_payment_id
+, invoice_id
+, invoice_number
+, invoice_created_date
+, invoice_date
+, invoice_target_date
+, invoice_currency
+, invoice_balance
+, invoice_amount_paid
+, invoice_amount_charged
+, invoice_original_amount_charged
+, invoice_amount_credited
+, invoice_payment_type
+, payment_number
+, linked_invoice_payment_id
+, amount
+, currency
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :invoicePaymentRecordId
+, :invoicePaymentId
+, :invoiceId
+, :invoiceNumber
+, :invoiceCreatedDate
+, :invoiceDate
+, :invoiceTargetDate
+, :invoiceCurrency
+, :invoiceBalance
+, :invoiceAmountPaid
+, :invoiceAmountCharged
+, :invoiceOriginalAmountCharged
+, :invoiceAmountCredited
+, :invoicePaymentType
+, :paymentNumber
+, :linkedInvoicePaymentId
+, :amount
+, :currency
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBos() ::= <<
+insert into bos (
+  blocking_state_record_id
+, bundle_id
+, bundle_external_key
+, status
+, start_date
+, end_date
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :blockingStateRecordId
+, :bundleId
+, :bundleExternalKey
+, :status
+, :startDate
+, :endDate
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBacTags() ::= <<
+insert into bac_tags (
+  tag_record_id
+, name
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :tagRecordId
+, :name
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBinTags() ::= <<
+insert into bin_tags (
+  tag_record_id
+, invoice_id
+, name
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :tagRecordId
+, :invoiceId
+, :name
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBipTags() ::= <<
+insert into bip_tags (
+  tag_record_id
+, invoice_payment_id
+, name
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :tagRecordId
+, :invoicePaymentId
+, :name
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBacFields() ::= <<
+insert into bac_fields (
+  custom_field_record_id
+, name
+, value
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :customFieldRecordId
+, :name
+, :value
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBinFields() ::= <<
+insert into bin_fields (
+  custom_field_record_id
+, invoice_id
+, name
+, value
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+  :customFieldRecordId
+, :invoiceId
+, :name
+, :value
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+createBipFields() ::= <<
+insert into bip_fields (
+, custom_field_record_id
+, invoice_payment_id
+, name
+, value
+, created_date
+, created_by
+, created_reason_code
+, created_comments
+, account_id
+, account_name
+, account_external_key
+, account_record_id
+, tenant_record_id
+) values (
+, :customFieldRecordId
+, :invoicePaymentId
+, :name
+, :value
+, :createdDate
+, :createdBy
+, :createdReasonCode
+, :createdComments
+, :accountId
+, :accountName
+, :accountExternalKey
+, :accountRecordId
+, :tenantRecordId
+);
+>>
+
+CHECK_TENANT_AND_ACCOUNT(prefix) ::= <<
+    <prefix>tenant_record_id \<=\> :tenantRecordId
+and <prefix>account_record_id \<=\> :accountRecordId
+>>
+
+SELECT_STAR_FROM_TABLE(table) ::= <<
+select *
+from <table> t
+where <CHECK_TENANT_AND_ACCOUNT("t.")>
+>>
+
+deleteByAccountRecordId(tableName) ::= <<
+delete from <tableName>
+where <CHECK_TENANT_AND_ACCOUNT("")>
+;
+>>
+
+getAccountByAccountRecordId() ::= <<
+<SELECT_STAR_FROM_TABLE("bac")>
+;
+>>
+
+getSubscriptionTransitionsByAccountRecordId() ::= <<
+<SELECT_STAR_FROM_TABLE("bst")>
+;
+>>
+
+getOverdueStatusesByAccountRecordId() ::= <<
+<SELECT_STAR_FROM_TABLE("bos")>
+;
+>>
+
+getInvoicesByAccountRecordId() ::= <<
+<SELECT_STAR_FROM_TABLE("bin")>
+;
+>>
+
+getInvoiceItemsByAccountRecordId() ::= <<
+<SELECT_STAR_FROM_TABLE("bia")>
+union all
+<SELECT_STAR_FROM_TABLE("bii")>
+union all
+<SELECT_STAR_FROM_TABLE("biia")>
+union all
+<SELECT_STAR_FROM_TABLE("biic")>
+;
+>>
+
+getInvoicePaymentsByAccountRecordId() ::= <<
+<SELECT_STAR_FROM_TABLE("bip")>
+union all
+<SELECT_STAR_FROM_TABLE("bipr")>
+union all
+<SELECT_STAR_FROM_TABLE("bipc")>
+;
+>>
+
+getAccountFieldsByAccountRecordId() ::= <<
+<SELECT_STAR_FROM_TABLE("bac_fields")>
+;
+>>
+
+getInvoiceFieldsByAccountRecordId() ::= <<
+<SELECT_STAR_FROM_TABLE("bin_fields")>
+;
+>>
+
+getInvoicePaymentFieldsByAccountRecordId() ::= <<
+<SELECT_STAR_FROM_TABLE("bip_fields")>
+;
+>>
+
+getAccountTagsByAccountRecordId() ::= <<
+<SELECT_STAR_FROM_TABLE("bac_tags")>
+;
+>>
+
+getInvoiceTagsByAccountRecordId() ::= <<
+<SELECT_STAR_FROM_TABLE("bin_tags")>
+;
+>>
+
+getInvoicePaymentTagsByAccountRecordId() ::= <<
+<SELECT_STAR_FROM_TABLE("bip_tags")>
+;
+>>
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..6f03b7b 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,14 @@
 /*! SET storage_engine=INNODB */;
 
+-- Subscription events
 drop table if exists bst;
 create table bst (
   record_id int(11) unsigned not null auto_increment
-, total_ordering bigint default 0
+, subscription_event_record_id int(11) unsigned default null
 , 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 +20,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 +33,170 @@ 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_account_record_id on bst(account_record_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
+, 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 default 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 index bac_account_external_key on bac(account_external_key);
+create index bac_account_id on bac(account_id);
+create index bac_account_record_id on bac(account_record_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 index bin_invoice_record_id on bin(invoice_record_id);
+create index bin_invoice_id on bin(invoice_id);
+create index bin_account_id on bin(account_id);
+create index bin_account_record_id on bin(account_record_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
+, second_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
+, revenue_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 bia_invoice_item_record_id on bia(invoice_item_record_id);
+create index bia_item_id on bia(item_id);
+create index bia_account_id on bia(account_id);
+create index bia_account_record_id on bia(account_record_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
+, second_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
+, revenue_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
@@ -108,152 +208,448 @@ 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 index bii_invoice_item_record_id on bii(invoice_item_record_id);
+create index bii_item_id on bii(item_id);
+create index bii_account_id on bii(account_id);
+create index bii_account_record_id on bii(account_record_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
+, second_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
+, revenue_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
-, 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 index biia_invoice_item_record_id on biia(invoice_item_record_id);
+create index biia_item_id on biia(item_id);
+create index biia_account_id on biia(account_id);
+create index biia_account_record_id on biia(account_record_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 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
+, second_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
+, revenue_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 index biic_invoice_item_record_id on biic(invoice_item_record_id);
+create index biic_item_id on biic(item_id);
+create index biic_account_id on biic(account_id);
+create index biic_account_record_id on biic(account_record_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
+, plugin_created_date datetime default null
+, plugin_effective_date datetime default null
+, plugin_status varchar(255) default null
+, plugin_gateway_error varchar(255) default null
+, plugin_gateway_error_code varchar(255) default null
+, plugin_first_reference_id varchar(255) default null
+, plugin_second_reference_id varchar(255) default null
+, plugin_pm_id varchar(255) default null
+, plugin_pm_is_default bool default null
+, plugin_pm_type varchar(255) default null
+, plugin_pm_cc_name varchar(255) default null
+, plugin_pm_cc_type varchar(255) default null
+, plugin_pm_cc_expiration_month varchar(255) default null
+, plugin_pm_cc_expiration_year varchar(255) default null
+, plugin_pm_cc_last_4 varchar(255) default null
+, plugin_pm_address1 varchar(255) default null
+, plugin_pm_address2 varchar(255) default null
+, plugin_pm_city varchar(255) default null
+, plugin_pm_state varchar(255) default null
+, plugin_pm_zip varchar(255) default null
+, plugin_pm_country 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_invoice_payment_record_id on bip(invoice_payment_record_id);
+create index bip_invoice_payment_id on bip(invoice_payment_id);
+create index bip_account_id on bip(account_id);
+create index bip_account_record_id on bip(account_record_id);
+create index bip_tenant_account_record_id on bip(tenant_record_id, account_record_id);
+
+-- 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
+, 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
+, plugin_created_date datetime default null
+, plugin_effective_date datetime default null
+, plugin_status varchar(255) default null
+, plugin_gateway_error varchar(255) default null
+, plugin_gateway_error_code varchar(255) default null
+, plugin_first_reference_id varchar(255) default null
+, plugin_second_reference_id varchar(255) default null
+, plugin_pm_id varchar(255) default null
+, plugin_pm_is_default bool default null
+, plugin_pm_type varchar(255) default null
+, plugin_pm_cc_name varchar(255) default null
+, plugin_pm_cc_type varchar(255) default null
+, plugin_pm_cc_expiration_month varchar(255) default null
+, plugin_pm_cc_expiration_year varchar(255) default null
+, plugin_pm_cc_last_4 varchar(255) default null
+, plugin_pm_address1 varchar(255) default null
+, plugin_pm_address2 varchar(255) default null
+, plugin_pm_city varchar(255) default null
+, plugin_pm_state varchar(255) default null
+, plugin_pm_zip varchar(255) default null
+, plugin_pm_country 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_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 index bipr_invoice_payment_record_id on bipr(invoice_payment_record_id);
+create index bipr_invoice_payment_id on bipr(invoice_payment_id);
+create index bipr_account_id on bipr(account_id);
+create index bipr_account_record_id on bipr(account_record_id);
+create index bipr_tenant_account_record_id on bipr(tenant_record_id, account_record_id);
 
-drop table if exists bac_fields;
-create table bac_fields (
+-- Invoice payment chargebacks
+drop table if exists bipc;
+create table bipc (
   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
+, plugin_created_date datetime default null
+, plugin_effective_date datetime default null
+, plugin_status varchar(255) default null
+, plugin_gateway_error varchar(255) default null
+, plugin_gateway_error_code varchar(255) default null
+, plugin_first_reference_id varchar(255) default null
+, plugin_second_reference_id varchar(255) default null
+, plugin_pm_id varchar(255) default null
+, plugin_pm_is_default bool default null
+, plugin_pm_type varchar(255) default null
+, plugin_pm_cc_name varchar(255) default null
+, plugin_pm_cc_type varchar(255) default null
+, plugin_pm_cc_expiration_month varchar(255) default null
+, plugin_pm_cc_expiration_year varchar(255) default null
+, plugin_pm_cc_last_4 varchar(255) default null
+, plugin_pm_address1 varchar(255) default null
+, plugin_pm_address2 varchar(255) default null
+, plugin_pm_city varchar(255) default null
+, plugin_pm_state varchar(255) default null
+, plugin_pm_zip varchar(255) default null
+, plugin_pm_country 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_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 index bipc_invoice_payment_record_id on bipc(invoice_payment_record_id);
+create index bipc_invoice_payment_id on bipc(invoice_payment_id);
+create index bipc_account_id on bipc(account_id);
+create index bipc_account_record_id on bipc(account_record_id);
+create index bipc_tenant_account_record_id on bipc(tenant_record_id, account_record_id);
 
-drop table if exists bst_tags;
-create table bst_tags (
+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'
-, name varchar(50) not null
+, 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 bst_tags_tenant_account_record_id on bst_tags(tenant_record_id, account_record_id);
+create index bos_account_id on bos(account_id);
+create index bos_account_record_id on bos(account_record_id);
+create index bos_tenant_account_record_id on bos(tenant_record_id, account_record_id);
 
-drop table if exists bst_fields;
-create table bst_fields (
+drop table if exists bac_tags;
+create table bac_tags (
   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'
+, 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_account_id on bac_tags(account_id);
+create index bac_tags_account_record_id on bac_tags(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_account_id on bin_tags(account_id);
+create index bin_tags_account_record_id on bin_tags(account_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_account_id on bip_tags(account_id);
+create index bip_tags_account_record_id on bip_tags(account_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_account_id on bac_fields(account_id);
+create index bac_fields_account_record_id on bac_fields(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_account_id on bin_fields(account_id);
+create index bin_fields_account_record_id on bin_fields(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)
 );
+create index bip_fields_account_id on bip_fields(account_id);
+create index bip_fields_account_record_id on bip_fields(account_record_id);
 create index bip_fields_tenant_account_record_id on bip_fields(tenant_record_id, account_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..3d50daf 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
@@ -16,124 +16,357 @@
 
 package com.ning.billing.osgi.bundles.analytics;
 
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeClass;
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.mockito.Mockito;
+import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 
-import com.ning.billing.GuicyKillbillTestSuiteNoDB;
-import com.ning.billing.account.api.AccountUserApi;
-import com.ning.billing.analytics.api.user.AnalyticsUserApi;
-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;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceFieldSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceItemSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoicePaymentFieldSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoicePaymentSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoicePaymentTagSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceTagSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessOverdueStatusSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessSubscriptionTransitionFieldSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessSubscriptionTransitionSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessSubscriptionTransitionTagSqlDao;
-import com.ning.billing.osgi.bundles.analytics.glue.TestAnalyticsModuleNoDB;
-import com.ning.billing.payment.dao.PaymentDao;
-import com.ning.billing.util.glue.RealImplementation;
-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.svcsapi.bus.InternalBus;
-
-import com.google.inject.Guice;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-
-public abstract class AnalyticsTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
-
-    @Inject
-    @RealImplementation
-    protected AccountUserApi accountApi;
-    @Inject
-    protected AccountInternalApi accountInternalApi;
-    @Inject
-    protected AnalyticsUserApi analyticsUserApi;
-    @Inject
-    protected CatalogService catalogService;
-    @Inject
-    @RealImplementation
-    protected EntitlementUserApi entitlementApi;
-    @Inject
-    protected EntitlementInternalApi entitlementInternalApi;
-    @Inject
-    protected InvoiceUserApi invoiceApi;
-    @Inject
-    protected InvoiceDao realInvoiceDao;
-    @Inject
-    protected InvoiceInternalApi invoiceInternalApi;
-    @Inject
-    protected PaymentDao paymentDao;
-    @Inject
-    protected DefaultAnalyticsService service;
-    @Inject
-    protected InternalBus bus;
-    @Inject
-    protected BusinessAccountDao accountDao;
-    @Inject
-    protected BusinessAccountSqlDao accountSqlDao;
-    @Inject
-    protected BusinessAccountFieldSqlDao accountFieldSqlDao;
-    @Inject
-    protected BusinessAccountTagSqlDao accountTagSqlDao;
-    @Inject
-    protected BusinessInvoiceFieldSqlDao invoiceFieldSqlDao;
-    @Inject
-    protected BusinessInvoiceItemSqlDao invoiceItemSqlDao;
-    @Inject
-    protected BusinessInvoicePaymentFieldSqlDao invoicePaymentFieldSqlDao;
-    @Inject
-    protected BusinessInvoicePaymentSqlDao invoicePaymentSqlDao;
-    @Inject
-    protected BusinessInvoicePaymentTagSqlDao invoicePaymentTagSqlDao;
-    @Inject
-    protected BusinessInvoiceDao invoiceDao;
-    @Inject
-    protected BusinessInvoiceSqlDao invoiceSqlDao;
-    @Inject
-    protected BusinessInvoiceTagSqlDao invoiceTagSqlDao;
-    @Inject
-    protected BusinessOverdueStatusDao overdueStatusDao;
-    @Inject
-    protected BusinessOverdueStatusSqlDao overdueStatusSqlDao;
-    @Inject
-    protected BusinessSubscriptionTransitionFieldSqlDao subscriptionTransitionFieldSqlDao;
-    @Inject
-    protected BusinessSubscriptionTransitionTagSqlDao subscriptionTransitionTagSqlDao;
-    @Inject
-    protected BusinessSubscriptionTransitionDao subscriptionTransitionDao;
-    @Inject
-    protected BusinessSubscriptionTransitionSqlDao subscriptionTransitionSqlDao;
-    @Inject
-    protected BusinessTagDao tagDao;
-
-    @BeforeClass(groups = "fast")
-    protected void beforeClass() throws Exception {
-        final Injector injector = Guice.createInjector(new TestAnalyticsModuleNoDB(configSource));
-        injector.injectMembers(this);
+import com.ning.billing.ObjectType;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PriceList;
+import com.ning.billing.catalog.api.Product;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+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.invoice.api.InvoicePayment;
+import com.ning.billing.invoice.api.InvoicePayment.InvoicePaymentType;
+import com.ning.billing.junction.api.Blockable.Type;
+import com.ning.billing.junction.api.BlockingState;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessEntityBase;
+import com.ning.billing.osgi.bundles.analytics.dao.TestCallContext;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessModelDaoBase;
+import com.ning.billing.payment.api.Payment;
+import com.ning.billing.payment.api.Payment.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentMethod;
+import com.ning.billing.payment.api.PaymentMethodPlugin;
+import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.payment.api.Refund;
+import com.ning.billing.util.api.RecordIdApi;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
+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;
+
+public abstract class AnalyticsTestSuiteNoDB {
+
+    protected final Long accountRecordId = 1L;
+    protected final Long subscriptionEventRecordId = 2L;
+    protected final Long invoiceRecordId = 3L;
+    protected final Long invoiceItemRecordId = 4L;
+    protected final Long secondInvoiceItemRecordId = 24L;
+    protected final Long invoicePaymentRecordId = 5L;
+    protected final Long blockingStateRecordId = 6L;
+    protected final Long fieldRecordId = 7L;
+    protected final Long tagRecordId = 8L;
+    protected final Long tenantRecordId = 9L;
+
+    protected Account account;
+    protected SubscriptionBundle bundle;
+    protected Plan plan;
+    protected PlanPhase phase;
+    protected PriceList priceList;
+    protected SubscriptionTransition subscriptionTransition;
+    protected BlockingState blockingState;
+    protected Invoice invoice;
+    protected InvoiceItem invoiceItem;
+    protected InvoicePayment invoicePayment;
+    protected PaymentAttempt paymentAttempt;
+    protected PaymentMethod paymentMethod;
+    protected Payment payment;
+    protected Refund refund;
+    protected CustomField customField;
+    protected Tag tag;
+    protected TagDefinition tagDefinition;
+    protected AuditLog auditLog;
+    protected CallContext callContext;
+    protected OSGIKillbillLogService logService;
+    protected OSGIKillbillAPI killbillAPI;
+    protected OSGIKillbillDataSource killbillDataSource;
+
+    protected void verifyBusinessEntityBase(final BusinessEntityBase businessEntityBase) {
+        Assert.assertEquals(businessEntityBase.getCreatedBy(), auditLog.getUserName());
+        Assert.assertEquals(businessEntityBase.getCreatedReasonCode(), auditLog.getReasonCode());
+        Assert.assertEquals(businessEntityBase.getCreatedComments(), auditLog.getComment());
+        Assert.assertEquals(businessEntityBase.getAccountId(), account.getId());
+        Assert.assertEquals(businessEntityBase.getAccountName(), account.getName());
+        Assert.assertEquals(businessEntityBase.getAccountExternalKey(), account.getExternalKey());
     }
 
-    @BeforeMethod(groups = "fast")
-    public void beforeMethod() throws Exception {
-        bus.start();
+    protected void verifyBusinessModelDaoBase(final BusinessModelDaoBase businessModelDaoBase,
+                                              final Long accountRecordId,
+                                              final Long tenantRecordId) {
+        Assert.assertEquals(businessModelDaoBase.getCreatedBy(), auditLog.getUserName());
+        Assert.assertEquals(businessModelDaoBase.getCreatedReasonCode(), auditLog.getReasonCode());
+        Assert.assertEquals(businessModelDaoBase.getCreatedComments(), auditLog.getComment());
+        Assert.assertEquals(businessModelDaoBase.getAccountId(), account.getId());
+        Assert.assertEquals(businessModelDaoBase.getAccountName(), account.getName());
+        Assert.assertEquals(businessModelDaoBase.getAccountExternalKey(), account.getExternalKey());
+        Assert.assertEquals(businessModelDaoBase.getAccountRecordId(), accountRecordId);
+        Assert.assertEquals(businessModelDaoBase.getTenantRecordId(), tenantRecordId);
     }
 
-    @AfterMethod(groups = "fast")
-    public void afterMethod() throws Exception {
-        bus.stop();
+    @BeforeMethod(groups = "fast")
+    public void setUp() throws Exception {
+        account = Mockito.mock(Account.class);
+        Mockito.when(account.getId()).thenReturn(UUID.randomUUID());
+        Mockito.when(account.getExternalKey()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(account.getName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(account.getFirstNameLength()).thenReturn(4);
+        Mockito.when(account.getEmail()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(account.getBillCycleDayLocal()).thenReturn(2);
+        Mockito.when(account.getCurrency()).thenReturn(Currency.BRL);
+        Mockito.when(account.getPaymentMethodId()).thenReturn(UUID.randomUUID());
+        Mockito.when(account.getTimeZone()).thenReturn(DateTimeZone.forID("Europe/London"));
+        Mockito.when(account.getLocale()).thenReturn(UUID.randomUUID().toString().substring(0, 5));
+        Mockito.when(account.getAddress1()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(account.getAddress2()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(account.getCompanyName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(account.getCity()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(account.getStateOrProvince()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(account.getPostalCode()).thenReturn(UUID.randomUUID().toString().substring(0, 16));
+        Mockito.when(account.getCountry()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(account.getPhone()).thenReturn(UUID.randomUUID().toString().substring(0, 25));
+        Mockito.when(account.isMigrated()).thenReturn(true);
+        Mockito.when(account.isNotifiedForInvoices()).thenReturn(true);
+        Mockito.when(account.getCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 47, DateTimeZone.UTC));
+        Mockito.when(account.getUpdatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 48, DateTimeZone.UTC));
+        final UUID accountId = account.getId();
+
+        bundle = Mockito.mock(SubscriptionBundle.class);
+        Mockito.when(bundle.getId()).thenReturn(UUID.randomUUID());
+        Mockito.when(bundle.getAccountId()).thenReturn(accountId);
+        Mockito.when(bundle.getExternalKey()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(bundle.getCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 48, DateTimeZone.UTC));
+        final UUID bundleId = bundle.getId();
+
+        final Product product = Mockito.mock(Product.class);
+        Mockito.when(product.getName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(product.isRetired()).thenReturn(true);
+        Mockito.when(product.getCategory()).thenReturn(ProductCategory.STANDALONE);
+        Mockito.when(product.getCatalogName()).thenReturn(UUID.randomUUID().toString());
+
+        plan = Mockito.mock(Plan.class);
+        Mockito.when(plan.getProduct()).thenReturn(product);
+        Mockito.when(plan.getName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(plan.isRetired()).thenReturn(true);
+        Mockito.when(plan.getBillingPeriod()).thenReturn(BillingPeriod.QUARTERLY);
+        Mockito.when(plan.getEffectiveDateForExistingSubscriptons()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 59, DateTimeZone.UTC).toDate());
+        final String planName = plan.getName();
+
+        phase = Mockito.mock(PlanPhase.class);
+        Mockito.when(phase.getBillingPeriod()).thenReturn(BillingPeriod.QUARTERLY);
+        Mockito.when(phase.getName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(phase.getPlan()).thenReturn(plan);
+        Mockito.when(phase.getPhaseType()).thenReturn(PhaseType.DISCOUNT);
+        final String phaseName = phase.getName();
+
+        priceList = Mockito.mock(PriceList.class);
+        Mockito.when(priceList.getName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(priceList.isRetired()).thenReturn(false);
+
+        subscriptionTransition = Mockito.mock(SubscriptionTransition.class);
+        Mockito.when(subscriptionTransition.getSubscriptionId()).thenReturn(UUID.randomUUID());
+        Mockito.when(subscriptionTransition.getBundleId()).thenReturn(bundleId);
+        Mockito.when(subscriptionTransition.getNextState()).thenReturn(SubscriptionState.ACTIVE);
+        Mockito.when(subscriptionTransition.getNextPlan()).thenReturn(plan);
+        Mockito.when(subscriptionTransition.getNextPhase()).thenReturn(phase);
+        Mockito.when(subscriptionTransition.getNextPriceList()).thenReturn(priceList);
+        Mockito.when(subscriptionTransition.getRequestedTransitionTime()).thenReturn(new DateTime(2010, 1, 2, 3, 4, 5, DateTimeZone.UTC));
+        Mockito.when(subscriptionTransition.getEffectiveTransitionTime()).thenReturn(new DateTime(2011, 2, 3, 4, 5, 6, DateTimeZone.UTC));
+        Mockito.when(subscriptionTransition.getTransitionType()).thenReturn(SubscriptionTransitionType.CREATE);
+        Mockito.when(subscriptionTransition.getNextEventCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 49, DateTimeZone.UTC));
+        Mockito.when(subscriptionTransition.getNextEventId()).thenReturn(UUID.randomUUID());
+        final UUID subscriptionId = subscriptionTransition.getSubscriptionId();
+        final UUID nextEventId = subscriptionTransition.getNextEventId();
+
+        blockingState = Mockito.mock(BlockingState.class);
+        Mockito.when(blockingState.getId()).thenReturn(UUID.randomUUID());
+        Mockito.when(blockingState.getBlockedId()).thenReturn(bundleId);
+        Mockito.when(blockingState.getStateName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(blockingState.getType()).thenReturn(Type.SUBSCRIPTION_BUNDLE);
+        Mockito.when(blockingState.getTimestamp()).thenReturn(new DateTime(2010, 2, 2, 4, 22, 22, DateTimeZone.UTC));
+        Mockito.when(blockingState.isBlockBilling()).thenReturn(true);
+        Mockito.when(blockingState.isBlockChange()).thenReturn(false);
+        Mockito.when(blockingState.isBlockEntitlement()).thenReturn(true);
+        Mockito.when(blockingState.getDescription()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(blockingState.getService()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(blockingState.getCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 0, DateTimeZone.UTC));
+        final UUID blockingStateId = blockingState.getId();
+
+        invoiceItem = Mockito.mock(InvoiceItem.class);
+        Mockito.when(invoiceItem.getId()).thenReturn(UUID.randomUUID());
+        Mockito.when(invoiceItem.getInvoiceItemType()).thenReturn(InvoiceItemType.EXTERNAL_CHARGE);
+        Mockito.when(invoiceItem.getInvoiceId()).thenReturn(UUID.randomUUID());
+        Mockito.when(invoiceItem.getAccountId()).thenReturn(UUID.randomUUID());
+        Mockito.when(invoiceItem.getStartDate()).thenReturn(new LocalDate(1999, 9, 9));
+        Mockito.when(invoiceItem.getEndDate()).thenReturn(new LocalDate(2048, 1, 1));
+        Mockito.when(invoiceItem.getAmount()).thenReturn(new BigDecimal("12000"));
+        Mockito.when(invoiceItem.getCurrency()).thenReturn(Currency.EUR);
+        Mockito.when(invoiceItem.getDescription()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(invoiceItem.getBundleId()).thenReturn(bundleId);
+        Mockito.when(invoiceItem.getSubscriptionId()).thenReturn(subscriptionId);
+        Mockito.when(invoiceItem.getPlanName()).thenReturn(planName);
+        Mockito.when(invoiceItem.getPhaseName()).thenReturn(phaseName);
+        Mockito.when(invoiceItem.getRate()).thenReturn(new BigDecimal("1203"));
+        Mockito.when(invoiceItem.getLinkedItemId()).thenReturn(UUID.randomUUID());
+        Mockito.when(invoiceItem.getCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 51, DateTimeZone.UTC));
+        final UUID invoiceItemId = invoiceItem.getId();
+
+        final UUID invoiceId = UUID.randomUUID();
+
+        invoicePayment = Mockito.mock(InvoicePayment.class);
+        Mockito.when(invoicePayment.getId()).thenReturn(UUID.randomUUID());
+        Mockito.when(invoicePayment.getPaymentId()).thenReturn(UUID.randomUUID());
+        Mockito.when(invoicePayment.getType()).thenReturn(InvoicePaymentType.ATTEMPT);
+        Mockito.when(invoicePayment.getInvoiceId()).thenReturn(invoiceId);
+        Mockito.when(invoicePayment.getPaymentDate()).thenReturn(new DateTime(2003, 4, 12, 3, 34, 52, DateTimeZone.UTC));
+        Mockito.when(invoicePayment.getAmount()).thenReturn(BigDecimal.ONE);
+        Mockito.when(invoicePayment.getCurrency()).thenReturn(Currency.MXN);
+        Mockito.when(invoicePayment.getLinkedInvoicePaymentId()).thenReturn(UUID.randomUUID());
+        Mockito.when(invoicePayment.getPaymentCookieId()).thenReturn(UUID.randomUUID());
+        Mockito.when(invoicePayment.getCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 53, DateTimeZone.UTC));
+        final UUID invoicePaymentId = invoicePayment.getId();
+
+        invoice = Mockito.mock(Invoice.class);
+        Mockito.when(invoice.getId()).thenReturn(invoiceId);
+        Mockito.when(invoice.getInvoiceItems()).thenReturn(ImmutableList.<InvoiceItem>of(invoiceItem));
+        Mockito.when(invoice.getNumberOfItems()).thenReturn(1);
+        Mockito.when(invoice.getPayments()).thenReturn(ImmutableList.<InvoicePayment>of(invoicePayment));
+        Mockito.when(invoice.getNumberOfPayments()).thenReturn(1);
+        Mockito.when(invoice.getAccountId()).thenReturn(accountId);
+        Mockito.when(invoice.getInvoiceNumber()).thenReturn(42);
+        Mockito.when(invoice.getInvoiceDate()).thenReturn(new LocalDate(1954, 12, 1));
+        Mockito.when(invoice.getTargetDate()).thenReturn(new LocalDate(2017, 3, 4));
+        Mockito.when(invoice.getCurrency()).thenReturn(Currency.AUD);
+        Mockito.when(invoice.getPaidAmount()).thenReturn(BigDecimal.ZERO);
+        Mockito.when(invoice.getOriginalChargedAmount()).thenReturn(new BigDecimal("1922"));
+        Mockito.when(invoice.getChargedAmount()).thenReturn(new BigDecimal("100293"));
+        Mockito.when(invoice.getCBAAmount()).thenReturn(BigDecimal.TEN);
+        Mockito.when(invoice.getTotalAdjAmount()).thenReturn(new BigDecimal("192"));
+        Mockito.when(invoice.getCreditAdjAmount()).thenReturn(new BigDecimal("283"));
+        Mockito.when(invoice.getRefundAdjAmount()).thenReturn(new BigDecimal("384"));
+        Mockito.when(invoice.getBalance()).thenReturn(new BigDecimal("18376"));
+        Mockito.when(invoice.isMigrationInvoice()).thenReturn(false);
+        Mockito.when(invoice.getCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 53, DateTimeZone.UTC));
+
+        paymentAttempt = Mockito.mock(PaymentAttempt.class);
+        Mockito.when(paymentAttempt.getId()).thenReturn(UUID.randomUUID());
+        Mockito.when(paymentAttempt.getEffectiveDate()).thenReturn(new DateTime(2019, 12, 30, 10, 10, 10, DateTimeZone.UTC));
+        Mockito.when(paymentAttempt.getGatewayErrorCode()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(paymentAttempt.getGatewayErrorMsg()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(paymentAttempt.getPaymentStatus()).thenReturn(PaymentStatus.SUCCESS);
+        Mockito.when(paymentAttempt.getCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 54, DateTimeZone.UTC));
+
+        final PaymentMethodPlugin paymentMethodPlugin = Mockito.mock(PaymentMethodPlugin.class);
+        Mockito.when(paymentMethodPlugin.getExternalPaymentMethodId()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(paymentMethodPlugin.isDefaultPaymentMethod()).thenReturn(true);
+
+        paymentMethod = Mockito.mock(PaymentMethod.class);
+        Mockito.when(paymentMethod.getId()).thenReturn(UUID.randomUUID());
+        Mockito.when(paymentMethod.getAccountId()).thenReturn(accountId);
+        Mockito.when(paymentMethod.isActive()).thenReturn(true);
+        Mockito.when(paymentMethod.getPluginName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(paymentMethod.getPluginDetail()).thenReturn(paymentMethodPlugin);
+        Mockito.when(paymentMethod.getCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 55, DateTimeZone.UTC));
+        final UUID paymentMethodId = paymentMethod.getId();
+
+        payment = Mockito.mock(Payment.class);
+        Mockito.when(payment.getId()).thenReturn(UUID.randomUUID());
+        Mockito.when(payment.getAccountId()).thenReturn(accountId);
+        Mockito.when(payment.getInvoiceId()).thenReturn(invoiceId);
+        Mockito.when(payment.getPaymentMethodId()).thenReturn(paymentMethodId);
+        Mockito.when(payment.getPaymentNumber()).thenReturn(1);
+        Mockito.when(payment.getAmount()).thenReturn(new BigDecimal("199999"));
+        Mockito.when(payment.getPaidAmount()).thenReturn(new BigDecimal("199998"));
+        Mockito.when(payment.getEffectiveDate()).thenReturn(new DateTime(2019, 2, 3, 12, 12, 12, DateTimeZone.UTC));
+        Mockito.when(payment.getCurrency()).thenReturn(Currency.USD);
+        Mockito.when(payment.getPaymentStatus()).thenReturn(PaymentStatus.AUTO_PAY_OFF);
+        Mockito.when(payment.getAttempts()).thenReturn(ImmutableList.<PaymentAttempt>of(paymentAttempt));
+        Mockito.when(payment.getExtFirstPaymentIdRef()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(payment.getExtSecondPaymentIdRef()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(payment.getCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 56, DateTimeZone.UTC));
+
+        refund = Mockito.mock(Refund.class);
+        Mockito.when(refund.getId()).thenReturn(UUID.randomUUID());
+        Mockito.when(refund.getPaymentId()).thenReturn(UUID.randomUUID());
+        Mockito.when(refund.isAdjusted()).thenReturn(true);
+        Mockito.when(refund.getRefundAmount()).thenReturn(BigDecimal.TEN);
+        Mockito.when(refund.getCurrency()).thenReturn(Currency.BRL);
+        Mockito.when(refund.getEffectiveDate()).thenReturn(new DateTime(2015, 2, 2, 10, 56, 5, DateTimeZone.UTC));
+
+        customField = Mockito.mock(CustomField.class);
+        Mockito.when(customField.getId()).thenReturn(UUID.randomUUID());
+        Mockito.when(customField.getObjectId()).thenReturn(UUID.randomUUID());
+        Mockito.when(customField.getObjectType()).thenReturn(ObjectType.TENANT);
+        Mockito.when(customField.getFieldName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(customField.getFieldValue()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(customField.getCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 57, DateTimeZone.UTC));
+        final UUID fieldId = customField.getId();
+
+        tag = Mockito.mock(Tag.class);
+        Mockito.when(tag.getObjectId()).thenReturn(UUID.randomUUID());
+        Mockito.when(tag.getObjectType()).thenReturn(ObjectType.ACCOUNT);
+        Mockito.when(tag.getTagDefinitionId()).thenReturn(UUID.randomUUID());
+        Mockito.when(tag.getCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 58, DateTimeZone.UTC));
+        final UUID tagId = tag.getId();
+
+        tagDefinition = Mockito.mock(TagDefinition.class);
+        Mockito.when(tagDefinition.getId()).thenReturn(UUID.randomUUID());
+        Mockito.when(tagDefinition.getName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(tagDefinition.getDescription()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(tagDefinition.isControlTag()).thenReturn(false);
+        Mockito.when(tagDefinition.getApplicableObjectTypes()).thenReturn(ImmutableList.<ObjectType>of(ObjectType.INVOICE));
+        Mockito.when(tagDefinition.getCreatedDate()).thenReturn(new DateTime(2016, 1, 22, 10, 56, 59, DateTimeZone.UTC));
+
+        auditLog = Mockito.mock(AuditLog.class);
+        Mockito.when(auditLog.getId()).thenReturn(UUID.randomUUID());
+        Mockito.when(auditLog.getChangeType()).thenReturn(ChangeType.INSERT);
+        Mockito.when(auditLog.getUserName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(auditLog.getCreatedDate()).thenReturn(new DateTime(2012, 12, 31, 23, 59, 59, DateTimeZone.UTC));
+        Mockito.when(auditLog.getReasonCode()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(auditLog.getUserToken()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(auditLog.getComment()).thenReturn(UUID.randomUUID().toString());
+
+        // Real class for the binding to work with JDBI
+        callContext = new TestCallContext();
+        final UUID tenantId = callContext.getTenantId();
+
+        final RecordIdApi recordIdApi = Mockito.mock(RecordIdApi.class);
+        Mockito.when(recordIdApi.getRecordId(accountId, ObjectType.ACCOUNT, callContext)).thenReturn(accountRecordId);
+        Mockito.when(recordIdApi.getRecordId(nextEventId, ObjectType.SUBSCRIPTION_EVENT, callContext)).thenReturn(subscriptionEventRecordId);
+        Mockito.when(recordIdApi.getRecordId(invoiceId, ObjectType.INVOICE, callContext)).thenReturn(invoiceRecordId);
+        Mockito.when(recordIdApi.getRecordId(invoiceItemId, ObjectType.INVOICE_ITEM, callContext)).thenReturn(invoiceItemRecordId);
+        Mockito.when(recordIdApi.getRecordId(invoicePaymentId, ObjectType.INVOICE_PAYMENT, callContext)).thenReturn(invoicePaymentRecordId);
+        Mockito.when(recordIdApi.getRecordId(blockingStateId, ObjectType.BLOCKING_STATES, callContext)).thenReturn(blockingStateRecordId);
+        Mockito.when(recordIdApi.getRecordId(fieldId, ObjectType.CUSTOM_FIELD, callContext)).thenReturn(fieldRecordId);
+        Mockito.when(recordIdApi.getRecordId(tagId, ObjectType.TAG, callContext)).thenReturn(tagRecordId);
+        Mockito.when(recordIdApi.getRecordId(tenantId, ObjectType.TENANT, callContext)).thenReturn(tenantRecordId);
+
+        killbillAPI = Mockito.mock(OSGIKillbillAPI.class);
+        Mockito.when(killbillAPI.getRecordIdApi()).thenReturn(recordIdApi);
+
+        killbillDataSource = Mockito.mock(OSGIKillbillDataSource.class);
     }
 }
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..9f867fe 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
@@ -16,138 +16,86 @@
 
 package com.ning.billing.osgi.bundles.analytics;
 
-import org.testng.annotations.AfterMethod;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.sql.DataSource;
+
+import org.mockito.Mockito;
+import org.osgi.framework.BundleContext;
+import org.skife.jdbi.v2.DBI;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 
-import com.ning.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
-import com.ning.billing.account.api.AccountUserApi;
-import com.ning.billing.analytics.api.AnalyticsService;
-import com.ning.billing.analytics.api.user.AnalyticsUserApi;
-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;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceFieldSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceItemSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoicePaymentFieldSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoicePaymentSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoicePaymentTagSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceTagSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessOverdueStatusSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessSubscriptionTransitionFieldSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessSubscriptionTransitionSqlDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessSubscriptionTransitionTagSqlDao;
-import com.ning.billing.osgi.bundles.analytics.glue.TestAnalyticsModuleWithEmbeddedDB;
-import com.ning.billing.payment.dao.PaymentDao;
-import com.ning.billing.util.glue.RealImplementation;
-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.svcsapi.bus.InternalBus;
-
-import com.google.inject.Guice;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-
-public abstract class AnalyticsTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWithEmbeddedDB {
-
-    @Inject
-    @RealImplementation
-    protected AccountUserApi accountApi;
-    @Inject
-    protected AccountInternalApi accountInternalApi;
-    @Inject
-    protected AnalyticsUserApi analyticsUserApi;
-    @Inject
-    protected AnalyticsService analyticsService;
-    @Inject
-    protected CatalogService catalogService;
-    @Inject
-    @RealImplementation
-    protected EntitlementUserApi entitlementApi;
-    @Inject
-    protected EntitlementInternalApi entitlementInternalApi;
-    @Inject
-    protected InvoiceUserApi invoiceApi;
-    @Inject
-    protected InvoiceDao realInvoiceDao;
-    @Inject
-    protected InvoiceInternalApi invoiceInternalApi;
-    @Inject
-    protected PaymentDao paymentDao;
-    @Inject
-    protected DefaultAnalyticsService service;
-    @Inject
-    protected InternalBus bus;
-    @Inject
-    protected BusinessAccountDao accountDao;
-    @Inject
-    protected BusinessAccountSqlDao accountSqlDao;
-    @Inject
-    protected BusinessAccountFieldSqlDao accountFieldSqlDao;
-    @Inject
-    protected BusinessAccountTagSqlDao accountTagSqlDao;
-    @Inject
-    protected BusinessInvoiceFieldSqlDao invoiceFieldSqlDao;
-    @Inject
-    protected BusinessInvoiceItemSqlDao invoiceItemSqlDao;
-    @Inject
-    protected BusinessInvoicePaymentFieldSqlDao invoicePaymentFieldSqlDao;
-    @Inject
-    protected BusinessInvoicePaymentSqlDao invoicePaymentSqlDao;
-    @Inject
-    protected BusinessInvoicePaymentTagSqlDao invoicePaymentTagSqlDao;
-    @Inject
-    protected BusinessInvoiceDao invoiceDao;
-    @Inject
-    protected BusinessInvoiceSqlDao invoiceSqlDao;
-    @Inject
-    protected BusinessInvoiceTagSqlDao invoiceTagSqlDao;
-    @Inject
-    protected BusinessOverdueStatusDao overdueStatusDao;
-    @Inject
-    protected BusinessOverdueStatusSqlDao overdueStatusSqlDao;
-    @Inject
-    protected BusinessSubscriptionTransitionFieldSqlDao subscriptionTransitionFieldSqlDao;
-    @Inject
-    protected BusinessSubscriptionTransitionTagSqlDao subscriptionTransitionTagSqlDao;
-    @Inject
-    protected BusinessSubscriptionTransitionDao subscriptionTransitionDao;
-    @Inject
-    protected BusinessSubscriptionTransitionSqlDao subscriptionTransitionSqlDao;
-    @Inject
-    protected BusinessTagDao tagDao;
+import com.ning.billing.commons.embeddeddb.mysql.MySQLEmbeddedDB;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessAnalyticsSqlDao;
+import com.ning.billing.osgi.bundles.analytics.dao.BusinessDBIProvider;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillDataSource;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.CharStreams;
+import com.google.common.io.InputSupplier;
+import com.google.common.io.Resources;
+
+public abstract class AnalyticsTestSuiteWithEmbeddedDB extends AnalyticsTestSuiteNoDB {
+
+    protected MySQLEmbeddedDB embeddedDB;
+    protected DBI dbi;
+    protected BusinessAnalyticsSqlDao analyticsSqlDao;
 
     @BeforeClass(groups = "slow")
-    protected void beforeClass() throws Exception {
-        final Injector injector = Guice.createInjector(new TestAnalyticsModuleWithEmbeddedDB(configSource));
-        injector.injectMembers(this);
+    public void setUpClass() throws Exception {
+        embeddedDB = new MySQLEmbeddedDB();
+        embeddedDB.initialize();
+        embeddedDB.start();
     }
 
     @BeforeMethod(groups = "slow")
-    public void beforeMethod() throws Exception {
-        super.beforeMethod();
-        bus.start();
-        restartAnalyticsService();
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        killbillDataSource = new AnalyticsOSGIKillbillDataSource();
+
+        final String ddl = toString(Resources.getResource("com/ning/billing/osgi/bundles/analytics/ddl.sql").openStream());
+        embeddedDB.executeScript(ddl);
+
+        dbi = BusinessDBIProvider.get(embeddedDB.getDataSource());
+        analyticsSqlDao = dbi.onDemand(BusinessAnalyticsSqlDao.class);
     }
 
-    @AfterMethod(groups = "slow")
-    public void afterMethod() throws Exception {
-        bus.stop();
-        stopAnalyticsService();
+    @AfterClass(groups = "slow")
+    public void tearDown() throws Exception {
+        embeddedDB.stop();
     }
 
-    private void restartAnalyticsService() throws Exception {
-        ((DefaultAnalyticsService) analyticsService).registerForNotifications();
+    public static String toString(final InputStream stream) throws IOException {
+        final InputSupplier<InputStream> inputSupplier = new InputSupplier<InputStream>() {
+            @Override
+            public InputStream getInput() throws IOException {
+                return stream;
+            }
+        };
+
+        return CharStreams.toString(CharStreams.newReaderSupplier(inputSupplier, Charsets.UTF_8));
     }
 
-    private void stopAnalyticsService() throws Exception {
-        ((DefaultAnalyticsService) analyticsService).unregisterForNotifications();
+    private final class AnalyticsOSGIKillbillDataSource extends OSGIKillbillDataSource {
+
+        public AnalyticsOSGIKillbillDataSource() {
+            super(Mockito.mock(BundleContext.class));
+        }
+
+        @Override
+        public DataSource getDataSource() {
+            try {
+                return embeddedDB.getDataSource();
+            } catch (IOException e) {
+                Assert.fail(e.toString(), e);
+                return null;
+            }
+        }
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessAccount.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessAccount.java
new file mode 100644
index 0000000..d24f729
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessAccount.java
@@ -0,0 +1,65 @@
+/*
+ * 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.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountModelDao;
+
+public class TestBusinessAccount extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructor() throws Exception {
+        final BusinessAccountModelDao accountModelDao = new BusinessAccountModelDao(account,
+                                                                                    accountRecordId,
+                                                                                    BigDecimal.ONE,
+                                                                                    invoice,
+                                                                                    payment,
+                                                                                    auditLog,
+                                                                                    tenantRecordId);
+        final BusinessAccount businessAccount = new BusinessAccount(accountModelDao);
+
+        verifyBusinessEntityBase(businessAccount);
+        Assert.assertEquals(businessAccount.getCreatedDate(), accountModelDao.getCreatedDate());
+        Assert.assertEquals(businessAccount.getEmail(), accountModelDao.getEmail());
+        Assert.assertEquals(businessAccount.getFirstNameLength(), accountModelDao.getFirstNameLength());
+        Assert.assertEquals(businessAccount.getCurrency(), accountModelDao.getCurrency());
+        Assert.assertEquals(businessAccount.getBillingCycleDayLocal(), accountModelDao.getBillingCycleDayLocal());
+        Assert.assertEquals(businessAccount.getPaymentMethodId(), accountModelDao.getPaymentMethodId());
+        Assert.assertEquals(businessAccount.getTimeZone(), accountModelDao.getTimeZone());
+        Assert.assertEquals(businessAccount.getLocale(), accountModelDao.getLocale());
+        Assert.assertEquals(businessAccount.getAddress1(), accountModelDao.getAddress1());
+        Assert.assertEquals(businessAccount.getAddress2(), accountModelDao.getAddress2());
+        Assert.assertEquals(businessAccount.getCompanyName(), accountModelDao.getCompanyName());
+        Assert.assertEquals(businessAccount.getCity(), accountModelDao.getCity());
+        Assert.assertEquals(businessAccount.getStateOrProvince(), accountModelDao.getStateOrProvince());
+        Assert.assertEquals(businessAccount.getCountry(), accountModelDao.getCountry());
+        Assert.assertEquals(businessAccount.getPostalCode(), accountModelDao.getPostalCode());
+        Assert.assertEquals(businessAccount.getPhone(), accountModelDao.getPhone());
+        Assert.assertEquals(businessAccount.getMigrated(), accountModelDao.getMigrated());
+        Assert.assertEquals(businessAccount.getNotifiedForInvoices(), accountModelDao.getNotifiedForInvoices());
+        Assert.assertEquals(businessAccount.getUpdatedDate(), accountModelDao.getUpdatedDate());
+        Assert.assertEquals(businessAccount.getBalance(), accountModelDao.getBalance());
+        Assert.assertEquals(businessAccount.getLastInvoiceDate(), accountModelDao.getLastInvoiceDate());
+        Assert.assertEquals(businessAccount.getLastPaymentDate(), accountModelDao.getLastPaymentDate());
+        Assert.assertEquals(businessAccount.getLastPaymentStatus(), accountModelDao.getLastPaymentStatus());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessField.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessField.java
new file mode 100644
index 0000000..ec1db34
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessField.java
@@ -0,0 +1,75 @@
+/*
+ * 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 org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountFieldModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceFieldModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentFieldModelDao;
+
+public class TestBusinessField extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructorAccount() throws Exception {
+        final BusinessAccountFieldModelDao businessAccountFieldModelDao = new BusinessAccountFieldModelDao(account,
+                                                                                                           accountRecordId,
+                                                                                                           customField,
+                                                                                                           fieldRecordId,
+                                                                                                           auditLog,
+                                                                                                           tenantRecordId);
+        final BusinessField businessField = BusinessField.create(businessAccountFieldModelDao);
+        verifyBusinessField(businessField);
+        Assert.assertEquals(businessField.getObjectType(), ObjectType.ACCOUNT);
+    }
+
+    @Test(groups = "fast")
+    public void testConstructorInvoice() throws Exception {
+        final BusinessInvoiceFieldModelDao businessInvoiceFieldModelDao = new BusinessInvoiceFieldModelDao(account,
+                                                                                                           accountRecordId,
+                                                                                                           customField,
+                                                                                                           fieldRecordId,
+                                                                                                           auditLog,
+                                                                                                           tenantRecordId);
+        final BusinessField businessField = BusinessField.create(businessInvoiceFieldModelDao);
+        verifyBusinessField(businessField);
+        Assert.assertEquals(businessField.getObjectType(), ObjectType.INVOICE);
+    }
+
+    @Test(groups = "fast")
+    public void testConstructorPayment() throws Exception {
+        final BusinessInvoicePaymentFieldModelDao invoicePaymentFieldModelDao = new BusinessInvoicePaymentFieldModelDao(account,
+                                                                                                                        accountRecordId,
+                                                                                                                        customField,
+                                                                                                                        fieldRecordId,
+                                                                                                                        auditLog,
+                                                                                                                        tenantRecordId);
+        final BusinessField businessField = BusinessField.create(invoicePaymentFieldModelDao);
+        verifyBusinessField(businessField);
+        Assert.assertEquals(businessField.getObjectType(), ObjectType.INVOICE_PAYMENT);
+    }
+
+    private void verifyBusinessField(final BusinessField accountField) {
+        verifyBusinessEntityBase(accountField);
+        Assert.assertEquals(accountField.getCreatedDate(), customField.getCreatedDate());
+        Assert.assertEquals(accountField.getName(), customField.getFieldName());
+        Assert.assertEquals(accountField.getValue(), customField.getFieldValue());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoice.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoice.java
new file mode 100644
index 0000000..aefcef0
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoice.java
@@ -0,0 +1,95 @@
+/*
+ * 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 org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceItemBaseModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceModelDao;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestBusinessInvoice extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructor() throws Exception {
+        final BusinessInvoiceModelDao invoiceModelDao = new BusinessInvoiceModelDao(account,
+                                                                                    accountRecordId,
+                                                                                    invoice,
+                                                                                    invoiceRecordId,
+                                                                                    auditLog,
+                                                                                    tenantRecordId);
+        final BusinessInvoiceItemBaseModelDao invoiceItemBaseModelDao = BusinessInvoiceItemBaseModelDao.create(account,
+                                                                                                               accountRecordId,
+                                                                                                               invoice,
+                                                                                                               invoiceItem,
+                                                                                                               invoiceItemRecordId,
+                                                                                                               secondInvoiceItemRecordId,
+                                                                                                               bundle,
+                                                                                                               plan,
+                                                                                                               phase,
+                                                                                                               auditLog,
+                                                                                                               tenantRecordId);
+        final BusinessInvoice businessInvoice = new BusinessInvoice(invoiceModelDao,
+                                                                    ImmutableList.<BusinessInvoiceItemBaseModelDao>of(invoiceItemBaseModelDao));
+        verifyBusinessEntityBase(businessInvoice);
+        Assert.assertEquals(businessInvoice.getCreatedDate(), invoiceModelDao.getCreatedDate());
+        Assert.assertEquals(businessInvoice.getInvoiceId(), invoiceModelDao.getInvoiceId());
+        Assert.assertEquals(businessInvoice.getInvoiceNumber(), invoiceModelDao.getInvoiceNumber());
+        Assert.assertEquals(businessInvoice.getInvoiceDate(), invoiceModelDao.getInvoiceDate());
+        Assert.assertEquals(businessInvoice.getTargetDate(), invoiceModelDao.getTargetDate());
+        Assert.assertEquals(businessInvoice.getCurrency(), invoiceModelDao.getCurrency());
+        Assert.assertEquals(businessInvoice.getBalance(), invoiceModelDao.getBalance());
+        Assert.assertEquals(businessInvoice.getAmountPaid(), invoiceModelDao.getAmountPaid());
+        Assert.assertEquals(businessInvoice.getAmountCharged(), invoiceModelDao.getAmountCharged());
+        Assert.assertEquals(businessInvoice.getOriginalAmountCharged(), invoiceModelDao.getOriginalAmountCharged());
+        Assert.assertEquals(businessInvoice.getAmountCredited(), invoiceModelDao.getAmountCredited());
+        Assert.assertEquals(businessInvoice.getInvoiceItems().size(), 1);
+
+        final BusinessInvoiceItem businessInvoiceItem = businessInvoice.getInvoiceItems().get(0);
+        verifyBusinessEntityBase(businessInvoiceItem);
+        Assert.assertEquals(businessInvoiceItem.getCreatedDate(), invoiceItemBaseModelDao.getCreatedDate());
+        Assert.assertEquals(businessInvoiceItem.getItemId(), invoiceItemBaseModelDao.getItemId());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceId(), invoiceItemBaseModelDao.getInvoiceId());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceNumber(), invoiceItemBaseModelDao.getInvoiceNumber());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceCreatedDate(), invoiceItemBaseModelDao.getInvoiceCreatedDate());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceDate(), invoiceItemBaseModelDao.getInvoiceDate());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceTargetDate(), invoiceItemBaseModelDao.getInvoiceTargetDate());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceCurrency(), invoiceItemBaseModelDao.getInvoiceCurrency());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceBalance(), invoiceItemBaseModelDao.getInvoiceBalance());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceAmountPaid(), invoiceItemBaseModelDao.getInvoiceAmountPaid());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceAmountCharged(), invoiceItemBaseModelDao.getInvoiceAmountCharged());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceOriginalAmountCharged(), invoiceItemBaseModelDao.getInvoiceOriginalAmountCharged());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceAmountCredited(), invoiceItemBaseModelDao.getInvoiceAmountCredited());
+        Assert.assertEquals(businessInvoiceItem.getItemType(), invoiceItemBaseModelDao.getItemType());
+        Assert.assertEquals(businessInvoiceItem.getRecognizable(), invoiceItemBaseModelDao.getRevenueRecognizable());
+        Assert.assertEquals(businessInvoiceItem.getBundleExternalKey(), invoiceItemBaseModelDao.getBundleExternalKey());
+        Assert.assertEquals(businessInvoiceItem.getProductName(), invoiceItemBaseModelDao.getProductName());
+        Assert.assertEquals(businessInvoiceItem.getProductType(), invoiceItemBaseModelDao.getProductType());
+        Assert.assertEquals(businessInvoiceItem.getProductCategory(), invoiceItemBaseModelDao.getProductCategory());
+        Assert.assertEquals(businessInvoiceItem.getSlug(), invoiceItemBaseModelDao.getSlug());
+        Assert.assertEquals(businessInvoiceItem.getPhase(), invoiceItemBaseModelDao.getPhase());
+        Assert.assertEquals(businessInvoiceItem.getBillingPeriod(), invoiceItemBaseModelDao.getBillingPeriod());
+        Assert.assertEquals(businessInvoiceItem.getStartDate(), invoiceItemBaseModelDao.getStartDate());
+        Assert.assertEquals(businessInvoiceItem.getEndDate(), invoiceItemBaseModelDao.getEndDate());
+        Assert.assertEquals(businessInvoiceItem.getAmount(), invoiceItemBaseModelDao.getAmount());
+        Assert.assertEquals(businessInvoiceItem.getCurrency(), invoiceItemBaseModelDao.getCurrency());
+        Assert.assertEquals(businessInvoiceItem.getLinkedItemId(), invoiceItemBaseModelDao.getLinkedItemId());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoiceItem.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoiceItem.java
new file mode 100644
index 0000000..b3cad4c
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoiceItem.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceItemBaseModelDao;
+
+public class TestBusinessInvoiceItem extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructor() throws Exception {
+        final BusinessInvoiceItemBaseModelDao invoiceItemBaseModelDao = BusinessInvoiceItemBaseModelDao.create(account,
+                                                                                                               accountRecordId,
+                                                                                                               invoice,
+                                                                                                               invoiceItem,
+                                                                                                               invoiceItemRecordId,
+                                                                                                               secondInvoiceItemRecordId,
+                                                                                                               bundle,
+                                                                                                               plan,
+                                                                                                               phase,
+                                                                                                               auditLog,
+                                                                                                               tenantRecordId);
+        final BusinessInvoiceItem businessInvoiceItem = new BusinessInvoiceItem(invoiceItemBaseModelDao);
+        verifyBusinessEntityBase(businessInvoiceItem);
+        Assert.assertEquals(businessInvoiceItem.getCreatedDate(), invoiceItemBaseModelDao.getCreatedDate());
+        Assert.assertEquals(businessInvoiceItem.getItemId(), invoiceItemBaseModelDao.getItemId());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceId(), invoiceItemBaseModelDao.getInvoiceId());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceNumber(), invoiceItemBaseModelDao.getInvoiceNumber());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceCreatedDate(), invoiceItemBaseModelDao.getInvoiceCreatedDate());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceDate(), invoiceItemBaseModelDao.getInvoiceDate());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceTargetDate(), invoiceItemBaseModelDao.getInvoiceTargetDate());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceCurrency(), invoiceItemBaseModelDao.getInvoiceCurrency());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceBalance(), invoiceItemBaseModelDao.getInvoiceBalance());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceAmountPaid(), invoiceItemBaseModelDao.getInvoiceAmountPaid());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceAmountCharged(), invoiceItemBaseModelDao.getInvoiceAmountCharged());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceOriginalAmountCharged(), invoiceItemBaseModelDao.getInvoiceOriginalAmountCharged());
+        Assert.assertEquals(businessInvoiceItem.getInvoiceAmountCredited(), invoiceItemBaseModelDao.getInvoiceAmountCredited());
+        Assert.assertEquals(businessInvoiceItem.getItemType(), invoiceItemBaseModelDao.getItemType());
+        Assert.assertEquals(businessInvoiceItem.getRecognizable(), invoiceItemBaseModelDao.getRevenueRecognizable());
+        Assert.assertEquals(businessInvoiceItem.getBundleExternalKey(), invoiceItemBaseModelDao.getBundleExternalKey());
+        Assert.assertEquals(businessInvoiceItem.getProductName(), invoiceItemBaseModelDao.getProductName());
+        Assert.assertEquals(businessInvoiceItem.getProductType(), invoiceItemBaseModelDao.getProductType());
+        Assert.assertEquals(businessInvoiceItem.getProductCategory(), invoiceItemBaseModelDao.getProductCategory());
+        Assert.assertEquals(businessInvoiceItem.getSlug(), invoiceItemBaseModelDao.getSlug());
+        Assert.assertEquals(businessInvoiceItem.getPhase(), invoiceItemBaseModelDao.getPhase());
+        Assert.assertEquals(businessInvoiceItem.getBillingPeriod(), invoiceItemBaseModelDao.getBillingPeriod());
+        Assert.assertEquals(businessInvoiceItem.getStartDate(), invoiceItemBaseModelDao.getStartDate());
+        Assert.assertEquals(businessInvoiceItem.getEndDate(), invoiceItemBaseModelDao.getEndDate());
+        Assert.assertEquals(businessInvoiceItem.getAmount(), invoiceItemBaseModelDao.getAmount());
+        Assert.assertEquals(businessInvoiceItem.getCurrency(), invoiceItemBaseModelDao.getCurrency());
+        Assert.assertEquals(businessInvoiceItem.getLinkedItemId(), invoiceItemBaseModelDao.getLinkedItemId());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoicePayment.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoicePayment.java
new file mode 100644
index 0000000..c1b40b5
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessInvoicePayment.java
@@ -0,0 +1,61 @@
+/*
+ * 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 org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentBaseModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentModelDao;
+
+public class TestBusinessInvoicePayment extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructor() throws Exception {
+        final BusinessInvoicePaymentBaseModelDao invoicePaymentBaseModelDao = BusinessInvoicePaymentModelDao.create(account,
+                                                                                                                    accountRecordId,
+                                                                                                                    invoice,
+                                                                                                                    invoicePayment,
+                                                                                                                    invoicePaymentRecordId,
+                                                                                                                    payment,
+                                                                                                                    refund,
+                                                                                                                    paymentMethod,
+                                                                                                                    auditLog,
+                                                                                                                    tenantRecordId);
+        final BusinessInvoicePayment businessInvoicePayment = new BusinessInvoicePayment(invoicePaymentBaseModelDao);
+        verifyBusinessEntityBase(businessInvoicePayment);
+        Assert.assertEquals(businessInvoicePayment.getCreatedDate(), invoicePaymentBaseModelDao.getCreatedDate());
+        Assert.assertEquals(businessInvoicePayment.getInvoicePaymentId(), invoicePaymentBaseModelDao.getInvoicePaymentId());
+        Assert.assertEquals(businessInvoicePayment.getInvoiceId(), invoicePaymentBaseModelDao.getInvoiceId());
+        Assert.assertEquals(businessInvoicePayment.getInvoiceNumber(), invoicePaymentBaseModelDao.getInvoiceNumber());
+        Assert.assertEquals(businessInvoicePayment.getInvoiceCreatedDate(), invoicePaymentBaseModelDao.getInvoiceCreatedDate());
+        Assert.assertEquals(businessInvoicePayment.getInvoiceDate(), invoicePaymentBaseModelDao.getInvoiceDate());
+        Assert.assertEquals(businessInvoicePayment.getInvoiceTargetDate(), invoicePaymentBaseModelDao.getInvoiceTargetDate());
+        Assert.assertEquals(businessInvoicePayment.getInvoiceCurrency(), invoicePaymentBaseModelDao.getInvoiceCurrency());
+        Assert.assertEquals(businessInvoicePayment.getInvoiceBalance(), invoicePaymentBaseModelDao.getInvoiceBalance());
+        Assert.assertEquals(businessInvoicePayment.getInvoiceAmountPaid(), invoicePaymentBaseModelDao.getInvoiceAmountPaid());
+        Assert.assertEquals(businessInvoicePayment.getInvoiceAmountCharged(), invoicePaymentBaseModelDao.getInvoiceAmountCharged());
+        Assert.assertEquals(businessInvoicePayment.getInvoiceOriginalAmountCharged(), invoicePaymentBaseModelDao.getInvoiceOriginalAmountCharged());
+        Assert.assertEquals(businessInvoicePayment.getInvoiceAmountCredited(), invoicePaymentBaseModelDao.getInvoiceAmountCredited());
+        Assert.assertEquals(businessInvoicePayment.getInvoicePaymentType(), invoicePaymentBaseModelDao.getInvoicePaymentType());
+        Assert.assertEquals(businessInvoicePayment.getPaymentNumber(), invoicePaymentBaseModelDao.getPaymentNumber());
+        Assert.assertEquals(businessInvoicePayment.getLinkedInvoicePaymentId(), invoicePaymentBaseModelDao.getLinkedInvoicePaymentId());
+        Assert.assertEquals(businessInvoicePayment.getAmount(), invoicePaymentBaseModelDao.getAmount());
+        Assert.assertEquals(businessInvoicePayment.getCurrency(), invoicePaymentBaseModelDao.getCurrency());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessOverdueStatus.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessOverdueStatus.java
new file mode 100644
index 0000000..cfa7632
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessOverdueStatus.java
@@ -0,0 +1,49 @@
+/*
+ * 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 org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessOverdueStatusModelDao;
+
+public class TestBusinessOverdueStatus extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructor() throws Exception {
+        final DateTime endDate = new DateTime(2005, 6, 5, 4, 5, 6, DateTimeZone.UTC);
+        final BusinessOverdueStatusModelDao businessOverdueStatusModelDao = new BusinessOverdueStatusModelDao(account,
+                                                                                                              accountRecordId,
+                                                                                                              bundle,
+                                                                                                              blockingState,
+                                                                                                              blockingStateRecordId,
+                                                                                                              endDate,
+                                                                                                              auditLog,
+                                                                                                              tenantRecordId);
+        final BusinessOverdueStatus businessOverdueStatus = new BusinessOverdueStatus(businessOverdueStatusModelDao);
+        verifyBusinessEntityBase(businessOverdueStatus);
+        Assert.assertEquals(businessOverdueStatus.getCreatedDate(), businessOverdueStatusModelDao.getCreatedDate());
+        Assert.assertEquals(businessOverdueStatus.getObjectType(), ObjectType.BUNDLE.toString());
+        Assert.assertEquals(businessOverdueStatus.getStatus(), businessOverdueStatusModelDao.getStatus());
+        Assert.assertEquals(businessOverdueStatus.getStartDate(), businessOverdueStatusModelDao.getStartDate());
+        Assert.assertEquals(businessOverdueStatus.getEndDate(), businessOverdueStatusModelDao.getEndDate());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessSnapshot.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessSnapshot.java
new file mode 100644
index 0000000..7775be0
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessSnapshot.java
@@ -0,0 +1,169 @@
+/*
+ * 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.DateTimeZone;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountFieldModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountTagModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceItemBaseModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentBaseModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessOverdueStatusModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessSubscription;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessSubscriptionEvent;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessSubscriptionTransitionModelDao;
+import com.ning.billing.osgi.bundles.analytics.http.ObjectMapperProvider;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestBusinessSnapshot extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructor() throws Exception {
+        // Account
+        final BusinessAccountModelDao accountModelDao = new BusinessAccountModelDao(account,
+                                                                                    accountRecordId,
+                                                                                    BigDecimal.ONE,
+                                                                                    invoice,
+                                                                                    payment,
+                                                                                    auditLog,
+                                                                                    tenantRecordId);
+        final BusinessAccount businessAccount = new BusinessAccount(accountModelDao);
+
+        // Field
+        final BusinessAccountFieldModelDao businessAccountFieldModelDao = new BusinessAccountFieldModelDao(account,
+                                                                                                           accountRecordId,
+                                                                                                           customField,
+                                                                                                           fieldRecordId,
+                                                                                                           auditLog,
+                                                                                                           tenantRecordId);
+        final BusinessField businessField = BusinessField.create(businessAccountFieldModelDao);
+
+        // Invoice
+        final BusinessInvoiceModelDao invoiceModelDao = new BusinessInvoiceModelDao(account,
+                                                                                    accountRecordId,
+                                                                                    invoice,
+                                                                                    invoiceRecordId,
+                                                                                    auditLog,
+                                                                                    tenantRecordId);
+        final BusinessInvoiceItemBaseModelDao invoiceItemBaseModelDao = BusinessInvoiceItemBaseModelDao.create(account,
+                                                                                                               accountRecordId,
+                                                                                                               invoice,
+                                                                                                               invoiceItem,
+                                                                                                               invoiceItemRecordId,
+                                                                                                               secondInvoiceItemRecordId,
+                                                                                                               bundle,
+                                                                                                               plan,
+                                                                                                               phase,
+                                                                                                               auditLog,
+                                                                                                               tenantRecordId);
+        final BusinessInvoice businessInvoice = new BusinessInvoice(invoiceModelDao,
+                                                                    ImmutableList.<BusinessInvoiceItemBaseModelDao>of(invoiceItemBaseModelDao));
+
+        // Invoice payment
+        final BusinessInvoicePaymentBaseModelDao invoicePaymentBaseModelDao = BusinessInvoicePaymentModelDao.create(account,
+                                                                                                                    accountRecordId,
+                                                                                                                    invoice,
+                                                                                                                    invoicePayment,
+                                                                                                                    invoicePaymentRecordId,
+                                                                                                                    payment,
+                                                                                                                    refund,
+                                                                                                                    paymentMethod,
+                                                                                                                    auditLog,
+                                                                                                                    tenantRecordId);
+        final BusinessInvoicePayment businessInvoicePayment = new BusinessInvoicePayment(invoicePaymentBaseModelDao);
+
+        // Overdue
+        final DateTime endDate = new DateTime(2005, 6, 5, 4, 5, 6, DateTimeZone.UTC);
+        final BusinessOverdueStatusModelDao businessOverdueStatusModelDao = new BusinessOverdueStatusModelDao(account,
+                                                                                                              accountRecordId,
+                                                                                                              bundle,
+                                                                                                              blockingState,
+                                                                                                              blockingStateRecordId,
+                                                                                                              endDate,
+                                                                                                              auditLog,
+                                                                                                              tenantRecordId);
+        final BusinessOverdueStatus businessOverdueStatus = new BusinessOverdueStatus(businessOverdueStatusModelDao);
+
+        // Subscriptions
+        final DateTime startDate = new DateTime(2012, 6, 5, 4, 3, 12, DateTimeZone.UTC);
+        final DateTime requestedTimestamp = new DateTime(2012, 7, 21, 10, 10, 10, DateTimeZone.UTC);
+
+        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.valueOf("ADD_BASE");
+        final BusinessSubscription previousSubscription = null;
+        final BusinessSubscription nextSubscription = new BusinessSubscription(null, null, null, Currency.GBP, startDate, SubscriptionState.ACTIVE);
+        final BusinessSubscriptionTransitionModelDao subscriptionTransitionModelDao = new BusinessSubscriptionTransitionModelDao(account,
+                                                                                                                                 accountRecordId,
+                                                                                                                                 bundle,
+                                                                                                                                 subscriptionTransition,
+                                                                                                                                 subscriptionEventRecordId,
+                                                                                                                                 requestedTimestamp,
+                                                                                                                                 event,
+                                                                                                                                 previousSubscription,
+                                                                                                                                 nextSubscription,
+                                                                                                                                 auditLog,
+                                                                                                                                 tenantRecordId);
+        final BusinessSubscriptionTransition businessSubscriptionTransition = new BusinessSubscriptionTransition(subscriptionTransitionModelDao);
+
+        // Tag
+        final BusinessAccountTagModelDao businessAccountTagModelDao = new BusinessAccountTagModelDao(account,
+                                                                                                     accountRecordId,
+                                                                                                     tag,
+                                                                                                     tagRecordId,
+                                                                                                     tagDefinition,
+                                                                                                     auditLog,
+                                                                                                     tenantRecordId);
+        final BusinessTag businessTag = BusinessTag.create(businessAccountTagModelDao);
+
+        // Create the snapshot
+        final BusinessSnapshot businessSnapshot = new BusinessSnapshot(businessAccount,
+                                                                       ImmutableList.<BusinessSubscriptionTransition>of(businessSubscriptionTransition),
+                                                                       ImmutableList.<BusinessInvoice>of(businessInvoice),
+                                                                       ImmutableList.<BusinessInvoicePayment>of(businessInvoicePayment),
+                                                                       ImmutableList.<BusinessOverdueStatus>of(businessOverdueStatus),
+                                                                       ImmutableList.<BusinessTag>of(businessTag),
+                                                                       ImmutableList.<BusinessField>of(businessField));
+        Assert.assertEquals(businessSnapshot.getBusinessAccount(), businessAccount);
+        Assert.assertEquals(businessSnapshot.getBusinessSubscriptionTransitions().size(), 1);
+        Assert.assertEquals(businessSnapshot.getBusinessSubscriptionTransitions().iterator().next(), businessSubscriptionTransition);
+        Assert.assertEquals(businessSnapshot.getBusinessInvoices().size(), 1);
+        Assert.assertEquals(businessSnapshot.getBusinessInvoices().iterator().next(), businessInvoice);
+        Assert.assertEquals(businessSnapshot.getBusinessInvoicePayments().size(), 1);
+        Assert.assertEquals(businessSnapshot.getBusinessInvoicePayments().iterator().next(), businessInvoicePayment);
+        Assert.assertEquals(businessSnapshot.getBusinessOverdueStatuses().size(), 1);
+        Assert.assertEquals(businessSnapshot.getBusinessOverdueStatuses().iterator().next(), businessOverdueStatus);
+        Assert.assertEquals(businessSnapshot.getBusinessTags().size(), 1);
+        Assert.assertEquals(businessSnapshot.getBusinessTags().iterator().next(), businessTag);
+        Assert.assertEquals(businessSnapshot.getBusinessFields().size(), 1);
+        Assert.assertEquals(businessSnapshot.getBusinessFields().iterator().next(), businessField);
+
+        // We check we can write it out without exception - we can't deserialize it back (no annotation)
+        // but we don't care since the APIs are read-only for Analytics
+        final String asJson = ObjectMapperProvider.get().writeValueAsString(businessSnapshot);
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessSubscriptionTransition.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessSubscriptionTransition.java
new file mode 100644
index 0000000..be667ab
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessSubscriptionTransition.java
@@ -0,0 +1,91 @@
+/*
+ * 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 org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessSubscription;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessSubscriptionEvent;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessSubscriptionTransitionModelDao;
+
+public class TestBusinessSubscriptionTransition extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructor() throws Exception {
+        final DateTime startDate = new DateTime(2012, 6, 5, 4, 3, 12, DateTimeZone.UTC);
+        final DateTime requestedTimestamp = new DateTime(2012, 7, 21, 10, 10, 10, DateTimeZone.UTC);
+
+        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.valueOf("ADD_BASE");
+        final BusinessSubscription previousSubscription = null;
+        final BusinessSubscription nextSubscription = new BusinessSubscription(null, null, null, Currency.GBP, startDate, SubscriptionState.ACTIVE);
+        final BusinessSubscriptionTransitionModelDao subscriptionTransitionModelDao = new BusinessSubscriptionTransitionModelDao(account,
+                                                                                                                                 accountRecordId,
+                                                                                                                                 bundle,
+                                                                                                                                 subscriptionTransition,
+                                                                                                                                 subscriptionEventRecordId,
+                                                                                                                                 requestedTimestamp,
+                                                                                                                                 event,
+                                                                                                                                 previousSubscription,
+                                                                                                                                 nextSubscription,
+                                                                                                                                 auditLog,
+                                                                                                                                 tenantRecordId);
+        final BusinessSubscriptionTransition businessSubscriptionTransition = new BusinessSubscriptionTransition(subscriptionTransitionModelDao);
+
+        verifyBusinessEntityBase(businessSubscriptionTransition);
+        Assert.assertEquals(businessSubscriptionTransition.getCreatedDate(), subscriptionTransitionModelDao.getCreatedDate());
+        Assert.assertEquals(businessSubscriptionTransition.getBundleId(), subscriptionTransitionModelDao.getBundleId());
+        Assert.assertEquals(businessSubscriptionTransition.getBundleExternalKey(), subscriptionTransitionModelDao.getBundleExternalKey());
+        Assert.assertEquals(businessSubscriptionTransition.getSubscriptionId(), subscriptionTransitionModelDao.getSubscriptionId());
+        Assert.assertEquals(businessSubscriptionTransition.getRequestedTimestamp(), subscriptionTransitionModelDao.getRequestedTimestamp());
+        Assert.assertEquals(businessSubscriptionTransition.getEvent(), subscriptionTransitionModelDao.getEvent());
+
+        Assert.assertNull(businessSubscriptionTransition.getPrevProductName());
+        Assert.assertNull(businessSubscriptionTransition.getPrevProductType());
+        Assert.assertNull(businessSubscriptionTransition.getPrevProductCategory());
+        Assert.assertNull(businessSubscriptionTransition.getPrevSlug());
+        Assert.assertNull(businessSubscriptionTransition.getPrevPhase());
+        Assert.assertNull(businessSubscriptionTransition.getPrevBillingPeriod());
+        Assert.assertNull(businessSubscriptionTransition.getPrevPrice());
+        Assert.assertNull(businessSubscriptionTransition.getPrevPriceList());
+        Assert.assertNull(businessSubscriptionTransition.getPrevMrr());
+        Assert.assertNull(businessSubscriptionTransition.getPrevCurrency());
+        Assert.assertNull(businessSubscriptionTransition.getPrevBusinessActive());
+        Assert.assertNull(businessSubscriptionTransition.getPrevStartDate());
+        Assert.assertNull(businessSubscriptionTransition.getPrevState());
+
+        Assert.assertEquals(businessSubscriptionTransition.getNextProductName(), subscriptionTransitionModelDao.getNextProductName());
+        Assert.assertEquals(businessSubscriptionTransition.getNextProductType(), subscriptionTransitionModelDao.getNextProductType());
+        Assert.assertEquals(businessSubscriptionTransition.getNextProductCategory(), subscriptionTransitionModelDao.getNextProductCategory());
+        Assert.assertEquals(businessSubscriptionTransition.getNextSlug(), subscriptionTransitionModelDao.getNextSlug());
+        Assert.assertEquals(businessSubscriptionTransition.getNextPhase(), subscriptionTransitionModelDao.getNextPhase());
+        Assert.assertEquals(businessSubscriptionTransition.getNextBillingPeriod(), subscriptionTransitionModelDao.getNextBillingPeriod());
+        Assert.assertEquals(businessSubscriptionTransition.getNextPrice(), subscriptionTransitionModelDao.getNextPrice());
+        Assert.assertEquals(businessSubscriptionTransition.getNextPriceList(), subscriptionTransitionModelDao.getNextPriceList());
+        Assert.assertEquals(businessSubscriptionTransition.getNextMrr(), subscriptionTransitionModelDao.getNextMrr());
+        Assert.assertEquals(businessSubscriptionTransition.getNextCurrency(), subscriptionTransitionModelDao.getNextCurrency());
+        Assert.assertEquals(businessSubscriptionTransition.getNextBusinessActive(), subscriptionTransitionModelDao.getNextBusinessActive());
+        Assert.assertEquals(businessSubscriptionTransition.getNextStartDate(), subscriptionTransitionModelDao.getNextStartDate());
+        Assert.assertEquals(businessSubscriptionTransition.getNextEndDate(), subscriptionTransitionModelDao.getNextEndDate());
+        Assert.assertEquals(businessSubscriptionTransition.getNextState(), subscriptionTransitionModelDao.getNextState());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessTag.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessTag.java
new file mode 100644
index 0000000..8df62d7
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/TestBusinessTag.java
@@ -0,0 +1,77 @@
+/*
+ * 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 org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountTagModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoicePaymentTagModelDao;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessInvoiceTagModelDao;
+
+public class TestBusinessTag extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructorAccount() throws Exception {
+        final BusinessAccountTagModelDao businessAccountTagModelDao = new BusinessAccountTagModelDao(account,
+                                                                                                     accountRecordId,
+                                                                                                     tag,
+                                                                                                     tagRecordId,
+                                                                                                     tagDefinition,
+                                                                                                     auditLog,
+                                                                                                     tenantRecordId);
+        final BusinessTag businessTag = BusinessTag.create(businessAccountTagModelDao);
+        verifyBusinessTag(businessTag);
+        Assert.assertEquals(businessTag.getObjectType(), ObjectType.ACCOUNT);
+    }
+
+    @Test(groups = "fast")
+    public void testConstructorInvoice() throws Exception {
+        final BusinessInvoiceTagModelDao businessInvoiceTagModelDao = new BusinessInvoiceTagModelDao(account,
+                                                                                                     accountRecordId,
+                                                                                                     tag,
+                                                                                                     tagRecordId,
+                                                                                                     tagDefinition,
+                                                                                                     auditLog,
+                                                                                                     tenantRecordId);
+        final BusinessTag businessTag = BusinessTag.create(businessInvoiceTagModelDao);
+        verifyBusinessTag(businessTag);
+        Assert.assertEquals(businessTag.getObjectType(), ObjectType.INVOICE);
+    }
+
+    @Test(groups = "fast")
+    public void testConstructorPayment() throws Exception {
+        final BusinessInvoicePaymentTagModelDao invoicePaymentTagModelDao = new BusinessInvoicePaymentTagModelDao(account,
+                                                                                                                  accountRecordId,
+                                                                                                                  tag,
+                                                                                                                  tagRecordId,
+                                                                                                                  tagDefinition,
+                                                                                                                  auditLog,
+                                                                                                                  tenantRecordId);
+        final BusinessTag businessTag = BusinessTag.create(invoicePaymentTagModelDao);
+        verifyBusinessTag(businessTag);
+        Assert.assertEquals(businessTag.getObjectType(), ObjectType.INVOICE_PAYMENT);
+    }
+
+    private void verifyBusinessTag(final BusinessTag accountTag) {
+        verifyBusinessEntityBase(accountTag);
+        Assert.assertEquals(accountTag.getCreatedDate(), accountTag.getCreatedDate());
+        Assert.assertEquals(accountTag.getName(), accountTag.getName());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/user/TestDefaultAnalyticsUserApi.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/user/TestDefaultAnalyticsUserApi.java
index c7cb17d..0fdafdd 100644
--- a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/user/TestDefaultAnalyticsUserApi.java
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/api/user/TestDefaultAnalyticsUserApi.java
@@ -17,78 +17,30 @@
 package com.ning.billing.osgi.bundles.analytics.api.user;
 
 import java.math.BigDecimal;
-import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import com.ning.billing.analytics.api.TimeSeriesData;
-import com.ning.billing.catalog.api.Catalog;
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.catalog.api.PhaseType;
-import com.ning.billing.catalog.api.Plan;
-import com.ning.billing.catalog.api.PlanPhase;
-import com.ning.billing.catalog.api.Product;
-import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.mock.MockPlan;
 import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteWithEmbeddedDB;
-import com.ning.billing.osgi.bundles.analytics.MockDuration;
-import com.ning.billing.osgi.bundles.analytics.MockPhase;
-import com.ning.billing.osgi.bundles.analytics.MockProduct;
-import com.ning.billing.osgi.bundles.analytics.model.BusinessAccountModelDao;
-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.osgi.bundles.analytics.api.BusinessAccount;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessSnapshot;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountModelDao;
 
 public class TestDefaultAnalyticsUserApi extends AnalyticsTestSuiteWithEmbeddedDB {
 
-    @Test(groups = "mysql")
-    public void testAccountsCreatedOverTime() throws Exception {
-        final BusinessAccountModelDao account = new BusinessAccountModelDao(UUID.randomUUID(), UUID.randomUUID().toString(), UUID.randomUUID().toString(), BigDecimal.ONE, clock.getUTCToday(),
-                                                                            BigDecimal.TEN, "ERROR_NOT_ENOUGH_FUNDS", "CreditCard", "Visa", "FRANCE", "USD", clock.getUTCNow(), clock.getUTCNow());
-        accountSqlDao.createAccount(account, internalCallContext);
-
-        final TimeSeriesData data = analyticsUserApi.getAccountsCreatedOverTime(callContext);
-        Assert.assertEquals(data.getDates().size(), 1);
-        Assert.assertEquals(data.getDates().get(0), clock.getUTCToday());
-        Assert.assertEquals(data.getValues().size(), 1);
-        Assert.assertEquals(data.getValues().get(0), (double) 1);
-    }
-
-    @Test(groups = "mysql")
-    public void testSubscriptionsCreatedOverTime() throws Exception {
-        final String productType = "subscription";
-        final Product product = new MockProduct("platinum", productType, ProductCategory.BASE);
-        final Plan plan = new MockPlan("platinum-monthly", product);
-        final PlanPhase phase = new MockPhase(PhaseType.TRIAL, plan, MockDuration.UNLIMITED(), 25.95);
-        final Catalog catalog = Mockito.mock(Catalog.class);
-        Mockito.when(catalog.findPlan(Mockito.anyString(), Mockito.<DateTime>any(), Mockito.<DateTime>any())).thenReturn(plan);
-        Mockito.when(catalog.findPhase(Mockito.anyString(), Mockito.<DateTime>any(), Mockito.<DateTime>any())).thenReturn(phase);
-        final BusinessSubscriptionTransitionModelDao transition = new BusinessSubscriptionTransitionModelDao(
-                3L,
-                UUID.randomUUID(),
-                UUID.randomUUID().toString(),
-                UUID.randomUUID(),
-                UUID.randomUUID().toString(),
-                UUID.randomUUID(),
-                clock.getUTCNow(),
-                BusinessSubscriptionEvent.subscriptionCreated(plan.getName(), catalog, clock.getUTCNow(), clock.getUTCNow()),
-                null,
-                new BusinessSubscription("DEFAULT", plan.getName(), phase.getName(), Currency.USD, clock.getUTCNow(), Subscription.SubscriptionState.ACTIVE, catalog)
-        );
-        subscriptionTransitionSqlDao.createTransition(transition, internalCallContext);
-
-        final TimeSeriesData notFoundData = analyticsUserApi.getSubscriptionsCreatedOverTime(productType, UUID.randomUUID().toString(), callContext);
-        Assert.assertEquals(notFoundData.getDates().size(), 0);
-        Assert.assertEquals(notFoundData.getValues().size(), 0);
-
-        final TimeSeriesData data = analyticsUserApi.getSubscriptionsCreatedOverTime(productType, phase.getName(), callContext);
-        Assert.assertEquals(data.getDates().size(), 1);
-        Assert.assertEquals(data.getDates().get(0), clock.getUTCToday());
-        Assert.assertEquals(data.getValues().size(), 1);
-        Assert.assertEquals(data.getValues().get(0), (double) 1);
+    @Test(groups = "slow")
+    public void testAccountSnapshot() throws Exception {
+        final BusinessAccountModelDao accountModelDao = new BusinessAccountModelDao(account,
+                                                                                    accountRecordId,
+                                                                                    BigDecimal.ONE,
+                                                                                    invoice,
+                                                                                    payment,
+                                                                                    auditLog,
+                                                                                    tenantRecordId);
+        analyticsSqlDao.create(accountModelDao.getTableName(), accountModelDao, callContext);
+
+        final AnalyticsUserApi analyticsUserApi = new AnalyticsUserApi(logService, killbillAPI, killbillDataSource);
+        final BusinessSnapshot businessSnapshot = analyticsUserApi.getBusinessSnapshot(account.getId(), callContext);
+        Assert.assertEquals(businessSnapshot.getBusinessAccount(), new BusinessAccount(accountModelDao));
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessAccountModelDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessAccountModelDao.java
new file mode 100644
index 0000000..367a90a
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessAccountModelDao.java
@@ -0,0 +1,82 @@
+/*
+ * 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.dao.model;
+
+import java.math.BigDecimal;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+
+public class TestBusinessAccountModelDao extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructorWithNulls() throws Exception {
+        final BusinessAccountModelDao accountModelDao = new BusinessAccountModelDao(account,
+                                                                                    accountRecordId,
+                                                                                    BigDecimal.ONE,
+                                                                                    null,
+                                                                                    null,
+                                                                                    auditLog,
+                                                                                    tenantRecordId);
+        verifyAccountFields(accountModelDao);
+        Assert.assertEquals(accountModelDao.getBalance(), BigDecimal.ONE);
+        Assert.assertNull(accountModelDao.getLastInvoiceDate());
+        Assert.assertNull(accountModelDao.getLastPaymentDate());
+        Assert.assertNull(accountModelDao.getLastPaymentStatus());
+    }
+
+    @Test(groups = "fast")
+    public void testConstructorWithoutNulls() throws Exception {
+        final BusinessAccountModelDao accountModelDao = new BusinessAccountModelDao(account,
+                                                                                    accountRecordId,
+                                                                                    BigDecimal.ONE,
+                                                                                    invoice,
+                                                                                    payment,
+                                                                                    auditLog,
+                                                                                    tenantRecordId);
+        verifyAccountFields(accountModelDao);
+        Assert.assertEquals(accountModelDao.getBalance(), BigDecimal.ONE);
+        Assert.assertEquals(accountModelDao.getLastInvoiceDate(), invoice.getInvoiceDate());
+        Assert.assertEquals(accountModelDao.getLastPaymentDate(), payment.getEffectiveDate());
+        Assert.assertEquals(accountModelDao.getLastPaymentStatus(), payment.getPaymentStatus().toString());
+    }
+
+    private void verifyAccountFields(final BusinessAccountModelDao accountModelDao) {
+        verifyBusinessModelDaoBase(accountModelDao, accountRecordId, tenantRecordId);
+        Assert.assertEquals(accountModelDao.getCreatedDate(), account.getCreatedDate());
+        Assert.assertEquals(accountModelDao.getUpdatedDate(), account.getUpdatedDate());
+        Assert.assertEquals(accountModelDao.getEmail(), account.getEmail());
+        Assert.assertEquals(accountModelDao.getFirstNameLength(), account.getFirstNameLength());
+        Assert.assertEquals(accountModelDao.getCurrency(), account.getCurrency().toString());
+        Assert.assertEquals(accountModelDao.getBillingCycleDayLocal(), account.getBillCycleDayLocal());
+        Assert.assertEquals(accountModelDao.getPaymentMethodId(), account.getPaymentMethodId());
+        Assert.assertEquals(accountModelDao.getTimeZone(), account.getTimeZone().toString());
+        Assert.assertEquals(accountModelDao.getLocale(), account.getLocale());
+        Assert.assertEquals(accountModelDao.getAddress1(), account.getAddress1());
+        Assert.assertEquals(accountModelDao.getAddress2(), account.getAddress2());
+        Assert.assertEquals(accountModelDao.getCompanyName(), account.getCompanyName());
+        Assert.assertEquals(accountModelDao.getCity(), account.getCity());
+        Assert.assertEquals(accountModelDao.getStateOrProvince(), account.getStateOrProvince());
+        Assert.assertEquals(accountModelDao.getCountry(), account.getCountry());
+        Assert.assertEquals(accountModelDao.getPostalCode(), account.getPostalCode());
+        Assert.assertEquals(accountModelDao.getPhone(), account.getPhone());
+        Assert.assertEquals(accountModelDao.getMigrated(), account.isMigrated());
+        Assert.assertEquals(accountModelDao.getNotifiedForInvoices(), account.isNotifiedForInvoices());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoiceFieldModelDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoiceFieldModelDao.java
new file mode 100644
index 0000000..9cf0392
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoiceFieldModelDao.java
@@ -0,0 +1,41 @@
+/*
+ * 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.dao.model;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+
+public class TestBusinessInvoiceFieldModelDao extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructor() throws Exception {
+        final BusinessInvoiceFieldModelDao invoiceFieldModelDao = new BusinessInvoiceFieldModelDao(account,
+                                                                                                   accountRecordId,
+                                                                                                   customField,
+                                                                                                   fieldRecordId,
+                                                                                                   auditLog,
+                                                                                                   tenantRecordId);
+        verifyBusinessModelDaoBase(invoiceFieldModelDao, accountRecordId, tenantRecordId);
+        Assert.assertEquals(invoiceFieldModelDao.getCreatedDate(), customField.getCreatedDate());
+        Assert.assertEquals(invoiceFieldModelDao.getCustomFieldRecordId(), fieldRecordId);
+        Assert.assertEquals(invoiceFieldModelDao.getInvoiceId(), customField.getObjectId());
+        Assert.assertEquals(invoiceFieldModelDao.getName(), customField.getFieldName());
+        Assert.assertEquals(invoiceFieldModelDao.getValue(), customField.getFieldValue());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoiceItemModelDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoiceItemModelDao.java
new file mode 100644
index 0000000..fda8860
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoiceItemModelDao.java
@@ -0,0 +1,97 @@
+/*
+ * 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.dao.model;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+
+public class TestBusinessInvoiceItemModelDao extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructorWithNulls() throws Exception {
+        final BusinessInvoiceItemModelDao invoiceItemModelDao = new BusinessInvoiceItemModelDao(account,
+                                                                                                accountRecordId,
+                                                                                                invoice,
+                                                                                                invoiceItem,
+                                                                                                invoiceItemRecordId,
+                                                                                                secondInvoiceItemRecordId,
+                                                                                                null,
+                                                                                                null,
+                                                                                                null,
+                                                                                                auditLog,
+                                                                                                tenantRecordId);
+        verifyInvoiceItemFields(invoiceItemModelDao);
+        Assert.assertNull(invoiceItemModelDao.getBundleExternalKey());
+        Assert.assertNull(invoiceItemModelDao.getProductName());
+        Assert.assertNull(invoiceItemModelDao.getProductType());
+        Assert.assertNull(invoiceItemModelDao.getProductCategory());
+        Assert.assertNull(invoiceItemModelDao.getSlug());
+        Assert.assertNull(invoiceItemModelDao.getPhase());
+        Assert.assertNull(invoiceItemModelDao.getBillingPeriod());
+    }
+
+    @Test(groups = "fast")
+    public void testConstructorWithoutNulls() throws Exception {
+        final BusinessInvoiceItemModelDao invoiceItemModelDao = new BusinessInvoiceItemModelDao(account,
+                                                                                                accountRecordId,
+                                                                                                invoice,
+                                                                                                invoiceItem,
+                                                                                                invoiceItemRecordId,
+                                                                                                secondInvoiceItemRecordId,
+                                                                                                bundle,
+                                                                                                plan,
+                                                                                                phase,
+                                                                                                auditLog,
+                                                                                                tenantRecordId);
+        verifyInvoiceItemFields(invoiceItemModelDao);
+        Assert.assertEquals(invoiceItemModelDao.getBundleExternalKey(), bundle.getExternalKey());
+        Assert.assertEquals(invoiceItemModelDao.getProductName(), plan.getProduct().getName());
+        Assert.assertEquals(invoiceItemModelDao.getProductType(), plan.getProduct().getCatalogName());
+        Assert.assertEquals(invoiceItemModelDao.getProductCategory(), plan.getProduct().getCategory().toString());
+        Assert.assertEquals(invoiceItemModelDao.getSlug(), phase.getName());
+        Assert.assertEquals(invoiceItemModelDao.getPhase(), phase.getPhaseType().toString());
+        Assert.assertEquals(invoiceItemModelDao.getBillingPeriod(), phase.getBillingPeriod().toString());
+    }
+
+    private void verifyInvoiceItemFields(final BusinessInvoiceItemModelDao invoiceItemModelDao) {
+        verifyBusinessModelDaoBase(invoiceItemModelDao, accountRecordId, tenantRecordId);
+        Assert.assertEquals(invoiceItemModelDao.getCreatedDate(), invoiceItem.getCreatedDate());
+        Assert.assertEquals(invoiceItemModelDao.getInvoiceItemRecordId(), invoiceItemRecordId);
+        Assert.assertEquals(invoiceItemModelDao.getSecondInvoiceItemRecordId(), secondInvoiceItemRecordId);
+        Assert.assertEquals(invoiceItemModelDao.getItemId(), invoiceItem.getId());
+        Assert.assertEquals(invoiceItemModelDao.getInvoiceId(), invoice.getId());
+        Assert.assertEquals(invoiceItemModelDao.getInvoiceNumber(), invoice.getInvoiceNumber());
+        Assert.assertEquals(invoiceItemModelDao.getInvoiceCreatedDate(), invoice.getCreatedDate());
+        Assert.assertEquals(invoiceItemModelDao.getInvoiceDate(), invoice.getInvoiceDate());
+        Assert.assertEquals(invoiceItemModelDao.getInvoiceTargetDate(), invoice.getTargetDate());
+        Assert.assertEquals(invoiceItemModelDao.getInvoiceCurrency(), invoice.getCurrency().toString());
+        Assert.assertEquals(invoiceItemModelDao.getInvoiceBalance(), invoice.getBalance());
+        Assert.assertEquals(invoiceItemModelDao.getInvoiceAmountPaid(), invoice.getPaidAmount());
+        Assert.assertEquals(invoiceItemModelDao.getInvoiceAmountCharged(), invoice.getChargedAmount());
+        Assert.assertEquals(invoiceItemModelDao.getInvoiceOriginalAmountCharged(), invoice.getOriginalChargedAmount());
+        Assert.assertEquals(invoiceItemModelDao.getInvoiceAmountCredited(), invoice.getCreditAdjAmount());
+        Assert.assertEquals(invoiceItemModelDao.getItemType(), invoiceItem.getInvoiceItemType().toString());
+        //Assert.assertEquals(invoiceItemModelDao.getRevenueRecognizable(), /* TODO */);
+        Assert.assertEquals(invoiceItemModelDao.getStartDate(), invoiceItem.getStartDate());
+        Assert.assertEquals(invoiceItemModelDao.getAmount(), invoiceItem.getAmount());
+        Assert.assertEquals(invoiceItemModelDao.getCurrency(), invoiceItem.getCurrency().toString());
+        Assert.assertEquals(invoiceItemModelDao.getLinkedItemId(), invoiceItem.getLinkedItemId());
+        Assert.assertEquals(invoiceItemModelDao.getEndDate(), invoiceItem.getEndDate());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoiceModelDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoiceModelDao.java
new file mode 100644
index 0000000..980c8f2
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoiceModelDao.java
@@ -0,0 +1,48 @@
+/*
+ * 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.dao.model;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+
+public class TestBusinessInvoiceModelDao extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructor() throws Exception {
+        final BusinessInvoiceModelDao invoiceModelDao = new BusinessInvoiceModelDao(account,
+                                                                                    accountRecordId,
+                                                                                    invoice,
+                                                                                    invoiceRecordId,
+                                                                                    auditLog,
+                                                                                    tenantRecordId);
+        verifyBusinessModelDaoBase(invoiceModelDao, accountRecordId, tenantRecordId);
+        Assert.assertEquals(invoiceModelDao.getCreatedDate(), invoice.getCreatedDate());
+        Assert.assertEquals(invoiceModelDao.getInvoiceRecordId(), invoiceRecordId);
+        Assert.assertEquals(invoiceModelDao.getInvoiceId(), invoice.getId());
+        Assert.assertEquals(invoiceModelDao.getInvoiceNumber(), invoice.getInvoiceNumber());
+        Assert.assertEquals(invoiceModelDao.getInvoiceDate(), invoice.getInvoiceDate());
+        Assert.assertEquals(invoiceModelDao.getTargetDate(), invoice.getTargetDate());
+        Assert.assertEquals(invoiceModelDao.getCurrency(), invoice.getCurrency().toString());
+        Assert.assertEquals(invoiceModelDao.getBalance(), invoice.getBalance());
+        Assert.assertEquals(invoiceModelDao.getAmountPaid(), invoice.getPaidAmount());
+        Assert.assertEquals(invoiceModelDao.getAmountCharged(), invoice.getChargedAmount());
+        Assert.assertEquals(invoiceModelDao.getOriginalAmountCharged(), invoice.getOriginalChargedAmount());
+        Assert.assertEquals(invoiceModelDao.getAmountCredited(), invoice.getCreditAdjAmount());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoicePaymentFieldModelDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoicePaymentFieldModelDao.java
new file mode 100644
index 0000000..554b56b
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoicePaymentFieldModelDao.java
@@ -0,0 +1,41 @@
+/*
+ * 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.dao.model;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+
+public class TestBusinessInvoicePaymentFieldModelDao extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructor() throws Exception {
+        final BusinessInvoicePaymentFieldModelDao invoicePaymentFieldModelDao = new BusinessInvoicePaymentFieldModelDao(account,
+                                                                                                                        accountRecordId,
+                                                                                                                        customField,
+                                                                                                                        fieldRecordId,
+                                                                                                                        auditLog,
+                                                                                                                        tenantRecordId);
+        verifyBusinessModelDaoBase(invoicePaymentFieldModelDao, accountRecordId, tenantRecordId);
+        Assert.assertEquals(invoicePaymentFieldModelDao.getCreatedDate(), customField.getCreatedDate());
+        Assert.assertEquals(invoicePaymentFieldModelDao.getCustomFieldRecordId(), fieldRecordId);
+        Assert.assertEquals(invoicePaymentFieldModelDao.getInvoicePaymentId(), customField.getObjectId());
+        Assert.assertEquals(invoicePaymentFieldModelDao.getName(), customField.getFieldName());
+        Assert.assertEquals(invoicePaymentFieldModelDao.getValue(), customField.getFieldValue());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoicePaymentModelDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoicePaymentModelDao.java
new file mode 100644
index 0000000..529af42
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoicePaymentModelDao.java
@@ -0,0 +1,59 @@
+/*
+ * 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.dao.model;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+
+public class TestBusinessInvoicePaymentModelDao extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructor() throws Exception {
+        final BusinessInvoicePaymentModelDao invoicePaymentModelDao = new BusinessInvoicePaymentModelDao(account,
+                                                                                                         accountRecordId,
+                                                                                                         invoice,
+                                                                                                         invoicePayment,
+                                                                                                         invoicePaymentRecordId,
+                                                                                                         payment,
+                                                                                                         refund,
+                                                                                                         paymentMethod,
+                                                                                                         auditLog,
+                                                                                                         tenantRecordId);
+        verifyBusinessModelDaoBase(invoicePaymentModelDao, accountRecordId, tenantRecordId);
+        Assert.assertEquals(invoicePaymentModelDao.getCreatedDate(), invoicePayment.getCreatedDate());
+        Assert.assertEquals(invoicePaymentModelDao.getInvoicePaymentRecordId(), invoicePaymentRecordId);
+        Assert.assertEquals(invoicePaymentModelDao.getInvoicePaymentId(), invoicePayment.getId());
+        Assert.assertEquals(invoicePaymentModelDao.getInvoiceId(), invoice.getId());
+        Assert.assertEquals(invoicePaymentModelDao.getInvoiceNumber(), invoice.getInvoiceNumber());
+        Assert.assertEquals(invoicePaymentModelDao.getInvoiceCreatedDate(), invoice.getCreatedDate());
+        Assert.assertEquals(invoicePaymentModelDao.getInvoiceDate(), invoice.getInvoiceDate());
+        Assert.assertEquals(invoicePaymentModelDao.getInvoiceTargetDate(), invoice.getTargetDate());
+        Assert.assertEquals(invoicePaymentModelDao.getInvoiceCurrency(), invoice.getCurrency().toString());
+        Assert.assertEquals(invoicePaymentModelDao.getInvoiceBalance(), invoice.getBalance());
+        Assert.assertEquals(invoicePaymentModelDao.getInvoiceAmountPaid(), invoice.getPaidAmount());
+        Assert.assertEquals(invoicePaymentModelDao.getInvoiceAmountCharged(), invoice.getChargedAmount());
+        Assert.assertEquals(invoicePaymentModelDao.getInvoiceOriginalAmountCharged(), invoice.getOriginalChargedAmount());
+        Assert.assertEquals(invoicePaymentModelDao.getInvoiceAmountCredited(), invoice.getCreditAdjAmount());
+        Assert.assertEquals(invoicePaymentModelDao.getInvoicePaymentType(), invoicePayment.getType().toString());
+        //Assert.assertEquals(invoicePaymentModelDao.getPaymentNumber(), /* TODO */);
+        Assert.assertEquals(invoicePaymentModelDao.getLinkedInvoicePaymentId(), invoicePayment.getLinkedInvoicePaymentId());
+        Assert.assertEquals(invoicePaymentModelDao.getAmount(), invoicePayment.getAmount());
+        Assert.assertEquals(invoicePaymentModelDao.getCurrency(), invoicePayment.getCurrency().toString());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoicePaymentTagModelDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoicePaymentTagModelDao.java
new file mode 100644
index 0000000..0d4bda1
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessInvoicePaymentTagModelDao.java
@@ -0,0 +1,41 @@
+/*
+ * 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.dao.model;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+
+public class TestBusinessInvoicePaymentTagModelDao extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructor() throws Exception {
+        final BusinessInvoicePaymentTagModelDao invoicePaymentTagModelDao = new BusinessInvoicePaymentTagModelDao(account,
+                                                                                                                  accountRecordId,
+                                                                                                                  tag,
+                                                                                                                  tagRecordId,
+                                                                                                                  tagDefinition,
+                                                                                                                  auditLog,
+                                                                                                                  tenantRecordId);
+        verifyBusinessModelDaoBase(invoicePaymentTagModelDao, accountRecordId, tenantRecordId);
+        Assert.assertEquals(invoicePaymentTagModelDao.getCreatedDate(), tag.getCreatedDate());
+        Assert.assertEquals(invoicePaymentTagModelDao.getTagRecordId(), tagRecordId);
+        Assert.assertEquals(invoicePaymentTagModelDao.getInvoicePaymentId(), tag.getObjectId());
+        Assert.assertEquals(invoicePaymentTagModelDao.getName(), tagDefinition.getName());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessOverdueStatusModelDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessOverdueStatusModelDao.java
new file mode 100644
index 0000000..83b41d9
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessOverdueStatusModelDao.java
@@ -0,0 +1,48 @@
+/*
+ * 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.dao.model;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+
+public class TestBusinessOverdueStatusModelDao extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testEquals() throws Exception {
+        final DateTime endDate = new DateTime(2012, 7, 21, 10, 10, 10, DateTimeZone.UTC);
+        final BusinessOverdueStatusModelDao overdueStatusModelDao = new BusinessOverdueStatusModelDao(account,
+                                                                                                      accountRecordId,
+                                                                                                      bundle,
+                                                                                                      blockingState,
+                                                                                                      blockingStateRecordId,
+                                                                                                      endDate,
+                                                                                                      auditLog,
+                                                                                                      tenantRecordId);
+        verifyBusinessModelDaoBase(overdueStatusModelDao, accountRecordId, tenantRecordId);
+        Assert.assertEquals(overdueStatusModelDao.getCreatedDate(), blockingState.getCreatedDate());
+        Assert.assertEquals(overdueStatusModelDao.getBlockingStateRecordId(), blockingStateRecordId);
+        Assert.assertEquals(overdueStatusModelDao.getBundleId(), bundle.getId());
+        Assert.assertEquals(overdueStatusModelDao.getBundleExternalKey(), bundle.getExternalKey());
+        Assert.assertEquals(overdueStatusModelDao.getStatus(), blockingState.getStateName());
+        Assert.assertEquals(overdueStatusModelDao.getStartDate(), blockingState.getTimestamp());
+        Assert.assertEquals(overdueStatusModelDao.getEndDate(), endDate);
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessSubscriptionEvent.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessSubscriptionEvent.java
new file mode 100644
index 0000000..2ce6975
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessSubscriptionEvent.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.dao.model;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+
+public class TestBusinessSubscriptionEvent extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testValueOf() throws Exception {
+        BusinessSubscriptionEvent event;
+
+        event = BusinessSubscriptionEvent.valueOf("ADD_ADD_ON");
+        Assert.assertEquals(event.getEventType(), BusinessSubscriptionEvent.EventType.ADD);
+        Assert.assertEquals(event.getCategory(), ProductCategory.ADD_ON);
+
+        event = BusinessSubscriptionEvent.valueOf("CANCEL_BASE");
+        Assert.assertEquals(event.getEventType(), BusinessSubscriptionEvent.EventType.CANCEL);
+        Assert.assertEquals(event.getCategory(), ProductCategory.BASE);
+
+        event = BusinessSubscriptionEvent.valueOf("SYSTEM_CANCEL_ADD_ON");
+        Assert.assertEquals(event.getEventType(), BusinessSubscriptionEvent.EventType.SYSTEM_CANCEL);
+        Assert.assertEquals(event.getCategory(), ProductCategory.ADD_ON);
+    }
+
+    @Test(groups = "fast")
+    public void testFromSubscription() throws Exception {
+        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.fromTransition(subscriptionTransition);
+        Assert.assertEquals(event.getEventType(), BusinessSubscriptionEvent.EventType.ADD);
+        Assert.assertEquals(event.getCategory(), subscriptionTransition.getNextPlan().getProduct().getCategory());
+        Assert.assertEquals(event.toString(), "ADD_" + plan.getProduct().getCategory().toString());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessSubscriptionTransitionModelDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessSubscriptionTransitionModelDao.java
new file mode 100644
index 0000000..1ade98c
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/model/TestBusinessSubscriptionTransitionModelDao.java
@@ -0,0 +1,87 @@
+/*
+ * 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.dao.model;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteNoDB;
+
+public class TestBusinessSubscriptionTransitionModelDao extends AnalyticsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructor() throws Exception {
+        final DateTime startDate = new DateTime(2012, 6, 5, 4, 3, 12, DateTimeZone.UTC);
+        final DateTime requestedTimestamp = new DateTime(2012, 7, 21, 10, 10, 10, DateTimeZone.UTC);
+
+        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.valueOf("ADD_BASE");
+        final BusinessSubscription previousSubscription = null;
+        final BusinessSubscription nextSubscription = new BusinessSubscription(null, null, null, Currency.GBP, startDate, SubscriptionState.ACTIVE);
+        final BusinessSubscriptionTransitionModelDao subscriptionTransitionModelDao = new BusinessSubscriptionTransitionModelDao(account,
+                                                                                                                                 accountRecordId,
+                                                                                                                                 bundle,
+                                                                                                                                 subscriptionTransition,
+                                                                                                                                 subscriptionEventRecordId,
+                                                                                                                                 requestedTimestamp,
+                                                                                                                                 event,
+                                                                                                                                 previousSubscription,
+                                                                                                                                 nextSubscription,
+                                                                                                                                 auditLog,
+                                                                                                                                 tenantRecordId);
+        verifyBusinessModelDaoBase(subscriptionTransitionModelDao, accountRecordId, tenantRecordId);
+        Assert.assertEquals(subscriptionTransitionModelDao.getCreatedDate(), subscriptionTransition.getNextEventCreatedDate());
+        Assert.assertEquals(subscriptionTransitionModelDao.getSubscriptionEventRecordId(), subscriptionEventRecordId);
+        Assert.assertEquals(subscriptionTransitionModelDao.getBundleId(), bundle.getId());
+        Assert.assertEquals(subscriptionTransitionModelDao.getBundleExternalKey(), bundle.getExternalKey());
+        Assert.assertEquals(subscriptionTransitionModelDao.getSubscriptionId(), subscriptionTransition.getSubscriptionId());
+        Assert.assertEquals(subscriptionTransitionModelDao.getRequestedTimestamp(), requestedTimestamp);
+        Assert.assertEquals(subscriptionTransitionModelDao.getEvent(), event.toString());
+
+        Assert.assertNull(subscriptionTransitionModelDao.getPrevProductName());
+        Assert.assertNull(subscriptionTransitionModelDao.getPrevProductType());
+        Assert.assertNull(subscriptionTransitionModelDao.getPrevProductCategory());
+        Assert.assertNull(subscriptionTransitionModelDao.getPrevSlug());
+        Assert.assertNull(subscriptionTransitionModelDao.getPrevPhase());
+        Assert.assertNull(subscriptionTransitionModelDao.getPrevBillingPeriod());
+        Assert.assertNull(subscriptionTransitionModelDao.getPrevPrice());
+        Assert.assertNull(subscriptionTransitionModelDao.getPrevPriceList());
+        Assert.assertNull(subscriptionTransitionModelDao.getPrevMrr());
+        Assert.assertNull(subscriptionTransitionModelDao.getPrevCurrency());
+        Assert.assertNull(subscriptionTransitionModelDao.getPrevBusinessActive());
+        Assert.assertNull(subscriptionTransitionModelDao.getPrevStartDate());
+        Assert.assertNull(subscriptionTransitionModelDao.getPrevState());
+
+        Assert.assertEquals(subscriptionTransitionModelDao.getNextProductName(), subscriptionTransitionModelDao.getNextProductName());
+        Assert.assertEquals(subscriptionTransitionModelDao.getNextProductType(), subscriptionTransitionModelDao.getNextProductType());
+        Assert.assertEquals(subscriptionTransitionModelDao.getNextProductCategory(), subscriptionTransitionModelDao.getNextProductCategory());
+        Assert.assertEquals(subscriptionTransitionModelDao.getNextSlug(), subscriptionTransitionModelDao.getNextSlug());
+        Assert.assertEquals(subscriptionTransitionModelDao.getNextPhase(), subscriptionTransitionModelDao.getNextPhase());
+        Assert.assertEquals(subscriptionTransitionModelDao.getNextBillingPeriod(), subscriptionTransitionModelDao.getNextBillingPeriod());
+        Assert.assertEquals(subscriptionTransitionModelDao.getNextPrice(), subscriptionTransitionModelDao.getNextPrice());
+        Assert.assertEquals(subscriptionTransitionModelDao.getNextPriceList(), subscriptionTransitionModelDao.getNextPriceList());
+        Assert.assertEquals(subscriptionTransitionModelDao.getNextMrr(), subscriptionTransitionModelDao.getNextMrr());
+        Assert.assertEquals(subscriptionTransitionModelDao.getNextCurrency(), subscriptionTransitionModelDao.getNextCurrency());
+        Assert.assertEquals(subscriptionTransitionModelDao.getNextBusinessActive(), subscriptionTransitionModelDao.getNextBusinessActive());
+        Assert.assertEquals(subscriptionTransitionModelDao.getNextStartDate(), subscriptionTransitionModelDao.getNextStartDate());
+        Assert.assertEquals(subscriptionTransitionModelDao.getNextEndDate(), subscriptionTransitionModelDao.getNextEndDate());
+        Assert.assertEquals(subscriptionTransitionModelDao.getNextState(), subscriptionTransitionModelDao.getNextState());
+    }
+}
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestAnalyticsDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestAnalyticsDao.java
index 07aa037..ddce78d 100644
--- a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestAnalyticsDao.java
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestAnalyticsDao.java
@@ -17,253 +17,36 @@
 package com.ning.billing.osgi.bundles.analytics.dao;
 
 import java.math.BigDecimal;
-import java.util.List;
-import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.mockito.Mockito;
 import org.testng.Assert;
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-import com.ning.billing.catalog.api.Catalog;
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.catalog.api.PhaseType;
-import com.ning.billing.catalog.api.Plan;
-import com.ning.billing.catalog.api.PlanPhase;
-import com.ning.billing.catalog.api.Product;
-import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.mock.MockPlan;
 import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteWithEmbeddedDB;
-import com.ning.billing.osgi.bundles.analytics.MockDuration;
-import com.ning.billing.osgi.bundles.analytics.MockPhase;
-import com.ning.billing.osgi.bundles.analytics.MockProduct;
-import com.ning.billing.osgi.bundles.analytics.model.BusinessAccountModelDao;
-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.osgi.bundles.analytics.utils.Rounder;
+import com.ning.billing.osgi.bundles.analytics.api.BusinessAccount;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountModelDao;
 
 public class TestAnalyticsDao extends AnalyticsTestSuiteWithEmbeddedDB {
 
-    private static final Long TOTAL_ORDERING = 1L;
-    private static final UUID BUNDLE_ID = UUID.randomUUID();
-    private static final String EXTERNAL_KEY = "23456";
-    private static final UUID ACCOUNT_ID = UUID.randomUUID();
-    private static final String ACCOUNT_KEY = "pierre-143343-vcc";
-    private static final String CURRENCY = UUID.randomUUID().toString();
-
-    private final Product product = new MockProduct("platinium", "subscription", ProductCategory.BASE);
-    private final Plan plan = new MockPlan("platinum-monthly", product);
-    private final PlanPhase phase = new MockPhase(PhaseType.EVERGREEN, plan, MockDuration.UNLIMITED(), 25.95);
-
-    private BusinessSubscriptionTransitionModelDao transition;
-    private BusinessAccountModelDao account;
-
-    private final Catalog catalog = Mockito.mock(Catalog.class);
-
-    @BeforeClass(groups = "slow")
-    public void beforeClass() throws Exception {
-        super.beforeClass();
-        Mockito.when(catalog.findPlan(Mockito.anyString(), Mockito.<DateTime>any())).thenReturn(plan);
-        Mockito.when(catalog.findPlan(Mockito.anyString(), Mockito.<DateTime>any(), Mockito.<DateTime>any())).thenReturn(plan);
-        Mockito.when(catalog.findPhase(Mockito.anyString(), Mockito.<DateTime>any(), Mockito.<DateTime>any())).thenReturn(phase);
-        Mockito.when(catalogService.getFullCatalog()).thenReturn(catalog);
-
-        setupBusinessSubscriptionTransition();
-        setupBusinessAccount();
-    }
-
-    private void setupBusinessSubscriptionTransition() {
-        final DateTime requestedTimestamp = clock.getUTCNow();
-        final BusinessSubscription prevSubscription = new BusinessSubscription(null, plan.getName(), phase.getName(), Currency.USD, clock.getUTCNow(), Subscription.SubscriptionState.ACTIVE, catalog);
-        final BusinessSubscription nextSubscription = new BusinessSubscription(null, plan.getName(), phase.getName(), Currency.USD, clock.getUTCNow(), Subscription.SubscriptionState.CANCELLED, catalog);
-        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(plan.getName(), catalog, requestedTimestamp, requestedTimestamp);
-
-        transition = new BusinessSubscriptionTransitionModelDao(TOTAL_ORDERING, BUNDLE_ID, EXTERNAL_KEY, ACCOUNT_ID, ACCOUNT_KEY,
-                                                                UUID.randomUUID(), requestedTimestamp, event, prevSubscription, nextSubscription);
-    }
-
-    private void setupBusinessAccount() {
-        account = new BusinessAccountModelDao(UUID.randomUUID(), ACCOUNT_KEY, UUID.randomUUID().toString(), BigDecimal.ONE, clock.getUTCToday(),
-                                              BigDecimal.TEN, "ERROR_NOT_ENOUGH_FUNDS", "CreditCard", "Visa", "FRANCE", CURRENCY, clock.getUTCNow(), clock.getUTCNow());
-    }
-
-    @Test(groups = "slow")
-    public void testTransitionsWithNullPrevSubscription() {
-        final BusinessSubscriptionTransitionModelDao transitionWithNullPrev = new BusinessSubscriptionTransitionModelDao(
-                transition.getTotalOrdering(),
-                transition.getBundleId(),
-                transition.getExternalKey(),
-                transition.getAccountId(),
-                transition.getAccountKey(),
-                transition.getSubscriptionId(),
-                transition.getRequestedTimestamp(),
-                transition.getEvent(),
-                null,
-                transition.getNextSubscription()
-        );
-        subscriptionTransitionSqlDao.createTransition(transitionWithNullPrev, internalCallContext);
-
-        final List<BusinessSubscriptionTransitionModelDao> transitions = subscriptionTransitionSqlDao.getTransitionsByKey(EXTERNAL_KEY, internalCallContext);
-        Assert.assertEquals(transitions.size(), 1);
-        Assert.assertEquals(transitions.get(0), transitionWithNullPrev);
-    }
-
     @Test(groups = "slow")
-    public void testTransitionsWithNullNextSubscription() {
-        final BusinessSubscriptionTransitionModelDao transitionWithNullNext = new BusinessSubscriptionTransitionModelDao(
-                transition.getTotalOrdering(),
-                transition.getBundleId(),
-                transition.getExternalKey(),
-                transition.getAccountId(),
-                transition.getAccountKey(),
-                transition.getSubscriptionId(),
-                transition.getRequestedTimestamp(),
-                transition.getEvent(),
-                transition.getPreviousSubscription(),
-                null
-        );
-        subscriptionTransitionSqlDao.createTransition(transitionWithNullNext, internalCallContext);
+    public void testDao() throws Exception {
+        final AnalyticsDao analyticsDao = new AnalyticsDao(killbillAPI, killbillDataSource);
+        Assert.assertNull(analyticsDao.getAccountById(account.getId(), callContext));
 
-        final List<BusinessSubscriptionTransitionModelDao> transitions = subscriptionTransitionSqlDao.getTransitionsByKey(EXTERNAL_KEY, internalCallContext);
-        Assert.assertEquals(transitions.size(), 1);
-        Assert.assertEquals(transitions.get(0), transitionWithNullNext);
-    }
-
-    @Test(groups = "slow")
-    public void testTransitionsWithNullFieldsInSubscription() {
-        final BusinessSubscription subscriptionWithNullFields = new BusinessSubscription(null, plan.getName(), phase.getName(), Currency.USD, null, null, catalog);
-        final BusinessSubscriptionTransitionModelDao transitionWithNullFields = new BusinessSubscriptionTransitionModelDao(
-                transition.getTotalOrdering(),
-                transition.getBundleId(),
-                transition.getExternalKey(),
-                transition.getAccountId(),
-                transition.getAccountKey(),
-                transition.getSubscriptionId(),
-                transition.getRequestedTimestamp(),
-                transition.getEvent(),
-                subscriptionWithNullFields,
-                subscriptionWithNullFields
-        );
-        subscriptionTransitionSqlDao.createTransition(transitionWithNullFields, internalCallContext);
-
-        final List<BusinessSubscriptionTransitionModelDao> transitions = subscriptionTransitionSqlDao.getTransitionsByKey(EXTERNAL_KEY, internalCallContext);
-        Assert.assertEquals(transitions.size(), 1);
-        Assert.assertEquals(transitions.get(0), transitionWithNullFields);
-    }
-
-    @Test(groups = "slow")
-    public void testTransitionsWithNullPlanAndPhase() throws Exception {
-        final BusinessSubscription subscriptionWithNullPlanAndPhase = new BusinessSubscription(null, null, null, Currency.USD, null, null, catalog);
-        final BusinessSubscriptionTransitionModelDao transitionWithNullPlanAndPhase = new BusinessSubscriptionTransitionModelDao(
-                transition.getTotalOrdering(),
-                transition.getBundleId(),
-                transition.getExternalKey(),
-                transition.getAccountId(),
-                transition.getAccountKey(),
-                transition.getSubscriptionId(),
-                transition.getRequestedTimestamp(),
-                transition.getEvent(),
-                subscriptionWithNullPlanAndPhase,
-                subscriptionWithNullPlanAndPhase
-        );
-        subscriptionTransitionSqlDao.createTransition(transitionWithNullPlanAndPhase, internalCallContext);
-
-        final List<BusinessSubscriptionTransitionModelDao> transitions = subscriptionTransitionSqlDao.getTransitionsByKey(EXTERNAL_KEY, internalCallContext);
-        Assert.assertEquals(transitions.size(), 1);
-        Assert.assertEquals(transitions.get(0).getExternalKey(), transition.getExternalKey());
-        Assert.assertEquals(transitions.get(0).getRequestedTimestamp(), transition.getRequestedTimestamp());
-        Assert.assertEquals(transitions.get(0).getEvent(), transition.getEvent());
-        Assert.assertNull(transitions.get(0).getPreviousSubscription());
-        Assert.assertNull(transitions.get(0).getNextSubscription());
-    }
-
-    @Test(groups = "slow")
-    public void testTransitionsWithNullPlan() throws Exception {
-        final BusinessSubscription subscriptionWithNullPlan = new BusinessSubscription(null, null, phase.getName(), Currency.USD, null, null, catalog);
-        final BusinessSubscriptionTransitionModelDao transitionWithNullPlan = new BusinessSubscriptionTransitionModelDao(
-                transition.getTotalOrdering(),
-                transition.getBundleId(),
-                transition.getExternalKey(),
-                transition.getAccountId(),
-                transition.getAccountKey(),
-                transition.getSubscriptionId(),
-                transition.getRequestedTimestamp(),
-                transition.getEvent(),
-                subscriptionWithNullPlan,
-                subscriptionWithNullPlan
-        );
-        subscriptionTransitionSqlDao.createTransition(transitionWithNullPlan, internalCallContext);
-
-        final List<BusinessSubscriptionTransitionModelDao> transitions = subscriptionTransitionSqlDao.getTransitionsByKey(EXTERNAL_KEY, internalCallContext);
-        Assert.assertEquals(transitions.size(), 1);
-        // Null Plan but Phase - we don't turn the subscription into a null
-        Assert.assertEquals(transitions.get(0), transitionWithNullPlan);
-    }
-
-    @Test(groups = "slow")
-    public void testTransitionsWithNullPhase() throws Exception {
-        final BusinessSubscription subscriptionWithNullPhase = new BusinessSubscription(null, plan.getName(), null, Currency.USD, null, null, catalog);
-        final BusinessSubscriptionTransitionModelDao transitionWithNullPhase = new BusinessSubscriptionTransitionModelDao(
-                transition.getTotalOrdering(),
-                transition.getBundleId(),
-                transition.getExternalKey(),
-                transition.getAccountId(),
-                transition.getAccountKey(),
-                transition.getSubscriptionId(),
-                transition.getRequestedTimestamp(),
-                transition.getEvent(),
-                subscriptionWithNullPhase,
-                subscriptionWithNullPhase
-        );
-        subscriptionTransitionSqlDao.createTransition(transitionWithNullPhase, internalCallContext);
-
-        final List<BusinessSubscriptionTransitionModelDao> transitions = subscriptionTransitionSqlDao.getTransitionsByKey(EXTERNAL_KEY, internalCallContext);
-        Assert.assertEquals(transitions.size(), 1);
-        Assert.assertEquals(transitions.get(0).getExternalKey(), transition.getExternalKey());
-        Assert.assertEquals(transitions.get(0).getRequestedTimestamp(), transition.getRequestedTimestamp());
-        Assert.assertEquals(transitions.get(0).getEvent(), transition.getEvent());
-
-        // Null Phase but Plan - we don't turn the subscription into a null, however price and mrr are both set to 0 (not null)
-        final BusinessSubscription blankSubscription = new BusinessSubscription(null, plan.getName(), new MockPhase(null, null, null, 0.0).getName(), Currency.USD, null, null, catalog);
-        Assert.assertEquals(transitions.get(0).getPreviousSubscription(), blankSubscription);
-        Assert.assertEquals(transitions.get(0).getNextSubscription(), blankSubscription);
-    }
-
-    @Test(groups = "slow")
-    public void testCreateAndRetrieveTransitions() {
-        subscriptionTransitionSqlDao.createTransition(transition, internalCallContext);
-
-        final List<BusinessSubscriptionTransitionModelDao> transitions = subscriptionTransitionSqlDao.getTransitionsByKey(EXTERNAL_KEY, internalCallContext);
-        Assert.assertEquals(transitions.size(), 1);
-        Assert.assertEquals(transitions.get(0), transition);
-
-        Assert.assertEquals(subscriptionTransitionSqlDao.getTransitionsByKey("Doesn't exist", internalCallContext).size(), 0);
-    }
-
-    @Test(groups = "slow")
-    public void testCreateSaveAndRetrieveAccounts() {
-        // Create and retrieve an account
-        accountSqlDao.createAccount(account, internalCallContext);
-        final BusinessAccountModelDao foundAccount = accountSqlDao.getAccountByKey(ACCOUNT_KEY, internalCallContext);
-        Assert.assertEquals(foundAccount.getCreatedDate().getMillis(), account.getCreatedDate().getMillis());
-        Assert.assertEquals(foundAccount.getUpdatedDate().getMillis(), account.getUpdatedDate().getMillis());
-        Assert.assertTrue(foundAccount.equals(account));
+        final BusinessAccountModelDao accountModelDao = new BusinessAccountModelDao(account,
+                                                                                    accountRecordId,
+                                                                                    BigDecimal.ONE,
+                                                                                    invoice,
+                                                                                    payment,
+                                                                                    auditLog,
+                                                                                    tenantRecordId);
 
-        // Try to update the account
-        account.setBalance(BigDecimal.TEN);
-        account.setPaymentMethod("PayPal");
-        account.setCurrency("CAD");
-        accountSqlDao.saveAccount(account, internalCallContext);
-        // Verify the save worked as expected
-        account = accountSqlDao.getAccountByKey(ACCOUNT_KEY, internalCallContext);
-        Assert.assertEquals(Rounder.round(BigDecimal.TEN), account.getRoundedBalance());
-        Assert.assertEquals("PayPal", account.getPaymentMethod());
-        Assert.assertEquals("CAD", account.getCurrency());
+        analyticsSqlDao.create(accountModelDao.getTableName(), accountModelDao, callContext);
+        Assert.assertEquals(analyticsDao.getAccountById(account.getId(), callContext), new BusinessAccount(accountModelDao));
 
-        // ACCOUNT not found
-        Assert.assertNull(accountSqlDao.getAccountByKey("Doesn't exist", internalCallContext));
+        analyticsSqlDao.deleteByAccountRecordId(accountModelDao.getTableName(),
+                                                accountModelDao.getAccountRecordId(),
+                                                accountModelDao.getTenantRecordId(),
+                                                callContext);
+        Assert.assertNull(analyticsDao.getAccountById(account.getId(), callContext));
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessAnalyticsSqlDao.java b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessAnalyticsSqlDao.java
new file mode 100644
index 0000000..f7564e6
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/test/java/com/ning/billing/osgi/bundles/analytics/dao/TestBusinessAnalyticsSqlDao.java
@@ -0,0 +1,56 @@
+/*
+ * 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.dao;
+
+import java.math.BigDecimal;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.osgi.bundles.analytics.AnalyticsTestSuiteWithEmbeddedDB;
+import com.ning.billing.osgi.bundles.analytics.dao.model.BusinessAccountModelDao;
+
+public class TestBusinessAnalyticsSqlDao extends AnalyticsTestSuiteWithEmbeddedDB {
+
+    @Test(groups = "slow")
+    public void testSqlDao() throws Exception {
+        final BusinessAccountModelDao accountModelDao = new BusinessAccountModelDao(account,
+                                                                                    accountRecordId,
+                                                                                    new BigDecimal("1.2345"),
+                                                                                    invoice,
+                                                                                    payment,
+                                                                                    auditLog,
+                                                                                    tenantRecordId);
+
+        Assert.assertNull(analyticsSqlDao.getAccountByAccountRecordId(accountModelDao.getAccountRecordId(),
+                                                                      accountModelDao.getTenantRecordId(),
+                                                                      callContext));
+
+        analyticsSqlDao.create(accountModelDao.getTableName(), accountModelDao, callContext);
+        Assert.assertEquals(analyticsSqlDao.getAccountByAccountRecordId(accountModelDao.getAccountRecordId(),
+                                                                        accountModelDao.getTenantRecordId(),
+                                                                        callContext), accountModelDao);
+
+        analyticsSqlDao.deleteByAccountRecordId(accountModelDao.getTableName(),
+                                                accountModelDao.getAccountRecordId(),
+                                                accountModelDao.getTenantRecordId(),
+                                                callContext);
+        Assert.assertNull(analyticsSqlDao.getAccountByAccountRecordId(accountModelDao.getAccountRecordId(),
+                                                                      accountModelDao.getTenantRecordId(),
+                                                                      callContext));
+    }
+}
diff --git a/osgi-bundles/bundles/pom.xml b/osgi-bundles/bundles/pom.xml
index 2b1d69c..1969482 100644
--- a/osgi-bundles/bundles/pom.xml
+++ b/osgi-bundles/bundles/pom.xml
@@ -27,6 +27,7 @@
     <name>Killbill billing platform: OSGI bundles</name>
     <packaging>pom</packaging>
     <modules>
+        <module>analytics</module>
         <module>jruby</module>
         <module>logger</module>
         <module>meter</module>

pom.xml 21(+21 -0)

diff --git a/pom.xml b/pom.xml
index 41f2baa..8b726fb 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>
@@ -285,6 +286,11 @@
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-osgi-bundles-analytics</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
                 <artifactId>killbill-osgi-bundles-jruby</artifactId>
                 <version>${project.version}</version>
             </dependency>
@@ -309,6 +315,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>
diff --git a/util/src/main/java/com/ning/billing/util/config/OSGIConfig.java b/util/src/main/java/com/ning/billing/util/config/OSGIConfig.java
index 4b97957..571929d 100644
--- a/util/src/main/java/com/ning/billing/util/config/OSGIConfig.java
+++ b/util/src/main/java/com/ning/billing/util/config/OSGIConfig.java
@@ -48,6 +48,7 @@ public interface OSGIConfig extends KillbillConfig {
              "com.ning.billing.analytics.api.user," +
              "com.ning.billing.beatrix.bus.api," +
              "com.ning.billing.catalog.api," +
+             "com.ning.billing.entitlement.api," +
              "com.ning.billing.entitlement.api.migration," +
              "com.ning.billing.entitlement.api.timeline," +
              "com.ning.billing.entitlement.api.transfer," +
@@ -62,7 +63,14 @@ public interface OSGIConfig extends KillbillConfig {
              "com.ning.billing.tenant.api," +
              "com.ning.billing.usage.api," +
              "com.ning.billing.util.api," +
+             "com.ning.billing.util.audit," +
              "com.ning.billing.util.callcontext," +
+             "com.ning.billing.util.customfield," +
+             "com.ning.billing.util.email," +
+             "com.ning.billing.util.entity," +
+             "com.ning.billing.util.tag," +
+             "com.ning.billing.util.template," +
+             "com.ning.billing.util.template.translation," +
              // javax.servlet and javax.servlet.http are not exported by default - we
              // need the bundles to see them for them to be able to register their servlets.
              // Note: bundles should mark javax.servlet:servlet-api as provided
diff --git a/util/src/main/java/com/ning/billing/util/dao/TableName.java b/util/src/main/java/com/ning/billing/util/dao/TableName.java
index 0e4da16..cc9aee0 100644
--- a/util/src/main/java/com/ning/billing/util/dao/TableName.java
+++ b/util/src/main/java/com/ning/billing/util/dao/TableName.java
@@ -29,7 +29,7 @@ public enum TableName {
     ACCOUNT_EMAIL_HISTORY("account_email_history"),
     ACCOUNT_EMAIL("account_emails", ObjectType.ACCOUNT_EMAIL, ACCOUNT_EMAIL_HISTORY),
     BUNDLES("bundles", ObjectType.BUNDLE),
-    BLOCKING_STATES("blocking_states"),
+    BLOCKING_STATES("blocking_states", ObjectType.BLOCKING_STATES),
     CUSTOM_FIELD_HISTORY("custom_field_history"),
     CUSTOM_FIELD("custom_fields", ObjectType.CUSTOM_FIELD, CUSTOM_FIELD_HISTORY),
     INVOICE_ITEMS("invoice_items", ObjectType.INVOICE_ITEM),