killbill-memoizeit

analytics: introduce BusinessAccount The BusinessAccount

11/11/2011 12:16:57 AM

Changes

analytics/pom.xml 12(+9 -3)

Details

analytics/pom.xml 12(+9 -3)

diff --git a/analytics/pom.xml b/analytics/pom.xml
index e6ef1de..32f9291 100644
--- a/analytics/pom.xml
+++ b/analytics/pom.xml
@@ -8,7 +8,8 @@
     OR CONDITIONS OF ANY KIND, either express or implied. See the ~ License for 
     the specific language governing permissions and limitations ~ under the License. -->
 
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>com.ning.billing</groupId>
@@ -21,8 +22,9 @@
     <packaging>jar</packaging>
     <dependencies>
         <dependency>
-            <groupId>org.jdbi</groupId>
-            <artifactId>jdbi</artifactId>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>com.google.inject</groupId>
@@ -63,6 +65,10 @@
             <scope>runtime</scope>
         </dependency>
         <dependency>
+            <groupId>org.jdbi</groupId>
+            <artifactId>jdbi</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
             <scope>test</scope>
diff --git a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
index 3d0bad2..63c7a06 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -19,7 +19,7 @@ package com.ning.billing.analytics;
 import com.google.inject.Inject;
 import com.ning.billing.account.api.IAccount;
 import com.ning.billing.account.api.IAccountUserApi;
-import com.ning.billing.analytics.dao.EventDao;
+import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.entitlement.api.user.IApiListener;
 import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
@@ -35,12 +35,12 @@ public class AnalyticsListener implements IApiListener
 {
     private static final Logger log = LoggerFactory.getLogger(AnalyticsListener.class);
 
-    private final EventDao dao;
+    private final BusinessSubscriptionTransitionDao dao;
     private final IEntitlementUserApi entitlementApi;
     private final IAccountUserApi accountApi;
 
     @Inject
-    public AnalyticsListener(final EventDao dao, final IEntitlementUserApi entitlementApi, final IAccountUserApi accountApi)
+    public AnalyticsListener(final BusinessSubscriptionTransitionDao dao, final IEntitlementUserApi entitlementApi, final IAccountUserApi accountApi)
     {
         this.dao = dao;
         this.entitlementApi = entitlementApi;
@@ -48,42 +48,42 @@ public class AnalyticsListener implements IApiListener
     }
 
     @Override
-    public void subscriptionCreated(ISubscriptionTransition created)
+    public void subscriptionCreated(final ISubscriptionTransition created)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCreated(created.getNextPlan());
         recordTransition(event, created);
     }
 
     @Override
-    public void subscriptionCancelled(ISubscriptionTransition cancelled)
+    public void subscriptionCancelled(final ISubscriptionTransition cancelled)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(cancelled.getNextPlan());
         recordTransition(event, cancelled);
     }
 
     @Override
-    public void subscriptionChanged(ISubscriptionTransition changed)
+    public void subscriptionChanged(final ISubscriptionTransition changed)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionChanged(changed.getNextPlan());
         recordTransition(event, changed);
     }
 
     @Override
-    public void subscriptionPaused(ISubscriptionTransition paused)
+    public void subscriptionPaused(final ISubscriptionTransition paused)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPaused(paused.getNextPlan());
         recordTransition(event, paused);
     }
 
     @Override
-    public void subscriptionResumed(ISubscriptionTransition resumed)
+    public void subscriptionResumed(final ISubscriptionTransition resumed)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionResumed(resumed.getNextPlan());
         recordTransition(event, resumed);
     }
 
     @Override
-    public void subscriptionPhaseChanged(ISubscriptionTransition phaseChanged)
+    public void subscriptionPhaseChanged(final ISubscriptionTransition phaseChanged)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPhaseChanged(phaseChanged.getNextPlan(), phaseChanged.getNextState());
         recordTransition(event, phaseChanged);
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccount.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccount.java
new file mode 100644
index 0000000..6103574
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccount.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics;
+
+import com.ning.billing.analytics.utils.Rounder;
+import org.joda.time.DateTime;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+public class BusinessAccount
+{
+    // Populated by the database
+    private DateTime createdDt = null;
+    private DateTime updatedDt = null;
+
+    private final String key;
+    private BigDecimal balance;
+    private List<String> tags;
+    private DateTime lastInvoiceDate;
+    private BigDecimal totalInvoiceBalance;
+    private String paymentMethod;
+    private String creditCardType;
+    private String billingAddressCountry;
+
+    public BusinessAccount(final String key, final BigDecimal balance, final List<String> tags, final DateTime lastInvoiceDate, final BigDecimal totalInvoiceBalance, final String paymentMethod, final String creditCardType, final String billingAddressCountry)
+    {
+        this.key = key;
+        this.balance = balance;
+        this.billingAddressCountry = billingAddressCountry;
+        this.creditCardType = creditCardType;
+        this.lastInvoiceDate = lastInvoiceDate;
+        this.paymentMethod = paymentMethod;
+        this.tags = tags;
+        this.totalInvoiceBalance = totalInvoiceBalance;
+    }
+
+    public String getKey()
+    {
+        return key;
+    }
+
+    public BigDecimal getBalance()
+    {
+        return balance;
+    }
+
+    public Double getRoundedBalance()
+    {
+        return Rounder.round(balance);
+    }
+
+    public void setBalance(final BigDecimal balance)
+    {
+        this.balance = balance;
+    }
+
+    public String getBillingAddressCountry()
+    {
+        return billingAddressCountry;
+    }
+
+    public void setBillingAddressCountry(final String billingAddressCountry)
+    {
+        this.billingAddressCountry = billingAddressCountry;
+    }
+
+    public DateTime getCreatedDt()
+    {
+        return createdDt;
+    }
+
+    public void setCreatedDt(final DateTime createdDt)
+    {
+        this.createdDt = createdDt;
+    }
+
+    public String getCreditCardType()
+    {
+        return creditCardType;
+    }
+
+    public void setCreditCardType(final String creditCardType)
+    {
+        this.creditCardType = creditCardType;
+    }
+
+    public DateTime getLastInvoiceDate()
+    {
+        return lastInvoiceDate;
+    }
+
+    public Double getRoundedTotalInvoiceBalance()
+    {
+        return Rounder.round(totalInvoiceBalance);
+    }
+
+    public void setLastInvoiceDate(final DateTime lastInvoiceDate)
+    {
+        this.lastInvoiceDate = lastInvoiceDate;
+    }
+
+    public String getPaymentMethod()
+    {
+        return paymentMethod;
+    }
+
+    public void setPaymentMethod(final String paymentMethod)
+    {
+        this.paymentMethod = paymentMethod;
+    }
+
+    public List<String> getTags()
+    {
+        return tags;
+    }
+
+    public void setTags(final List<String> tags)
+    {
+        this.tags = tags;
+    }
+
+    public BigDecimal getTotalInvoiceBalance()
+    {
+        return totalInvoiceBalance;
+    }
+
+    public void setTotalInvoiceBalance(final BigDecimal totalInvoiceBalance)
+    {
+        this.totalInvoiceBalance = totalInvoiceBalance;
+    }
+
+    public DateTime getUpdatedDt()
+    {
+        return updatedDt;
+    }
+
+    public void setUpdatedDt(final DateTime updatedDt)
+    {
+        this.updatedDt = updatedDt;
+    }
+
+    @Override
+    public String toString()
+    {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("BusinessAccount");
+        sb.append("{balance=").append(balance);
+        sb.append(", createdDt=").append(createdDt);
+        sb.append(", updatedDt=").append(updatedDt);
+        sb.append(", key='").append(key).append('\'');
+        sb.append(", tags=").append(tags);
+        sb.append(", lastInvoiceDate=").append(lastInvoiceDate);
+        sb.append(", totalInvoiceBalance=").append(totalInvoiceBalance);
+        sb.append(", paymentMethod='").append(paymentMethod).append('\'');
+        sb.append(", creditCardType='").append(creditCardType).append('\'');
+        sb.append(", billingAddressCountry='").append(billingAddressCountry).append('\'');
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o)
+    {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final BusinessAccount that = (BusinessAccount) o;
+
+        if (balance != null ? !(Rounder.round(balance) == Rounder.round(balance)) : that.balance != null) {
+            return false;
+        }
+        if (billingAddressCountry != null ? !billingAddressCountry.equals(that.billingAddressCountry) : that.billingAddressCountry != null) {
+            return false;
+        }
+        if (createdDt != null ? !createdDt.equals(that.createdDt) : that.createdDt != null) {
+            return false;
+        }
+        if (creditCardType != null ? !creditCardType.equals(that.creditCardType) : that.creditCardType != null) {
+            return false;
+        }
+        if (key != null ? !key.equals(that.key) : that.key != null) {
+            return false;
+        }
+        if (lastInvoiceDate != null ? !lastInvoiceDate.equals(that.lastInvoiceDate) : that.lastInvoiceDate != null) {
+            return false;
+        }
+        if (paymentMethod != null ? !paymentMethod.equals(that.paymentMethod) : that.paymentMethod != null) {
+            return false;
+        }
+        if (tags != null ? !tags.equals(that.tags) : that.tags != null) {
+            return false;
+        }
+        if (totalInvoiceBalance != null ? !(Rounder.round(totalInvoiceBalance) == Rounder.round(that.totalInvoiceBalance)) : that.totalInvoiceBalance != null) {
+            return false;
+        }
+        if (updatedDt != null ? !updatedDt.equals(that.updatedDt) : that.updatedDt != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int result = createdDt != null ? createdDt.hashCode() : 0;
+        result = 31 * result + (updatedDt != null ? updatedDt.hashCode() : 0);
+        result = 31 * result + (key != null ? key.hashCode() : 0);
+        result = 31 * result + (balance != null ? balance.hashCode() : 0);
+        result = 31 * result + (tags != null ? tags.hashCode() : 0);
+        result = 31 * result + (lastInvoiceDate != null ? lastInvoiceDate.hashCode() : 0);
+        result = 31 * result + (totalInvoiceBalance != null ? totalInvoiceBalance.hashCode() : 0);
+        result = 31 * result + (paymentMethod != null ? paymentMethod.hashCode() : 0);
+        result = 31 * result + (creditCardType != null ? creditCardType.hashCode() : 0);
+        result = 31 * result + (billingAddressCountry != null ? billingAddressCountry.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java
index 474c854..fbe2749 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.analytics;
 
+import com.ning.billing.analytics.utils.Rounder;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.IDuration;
 import com.ning.billing.catalog.api.IPlan;
@@ -44,7 +45,6 @@ public class BusinessSubscription
     private static final BigDecimal DAYS_IN_MONTH = BigDecimal.valueOf(30);
     private static final BigDecimal MONTHS_IN_YEAR = BigDecimal.valueOf(12);
     private static final Currency USD = Currency.valueOf("USD");
-    private static final int SCALE = 4;
 
     private final String productName;
     private final String productType;
@@ -162,7 +162,7 @@ public class BusinessSubscription
 
     public double getRoundedMrr()
     {
-        return round(mrr);
+        return Rounder.round(mrr);
     }
 
     public String getPhase()
@@ -177,7 +177,7 @@ public class BusinessSubscription
 
     public double getRoundedPrice()
     {
-        return round(price);
+        return Rounder.round(price);
     }
 
     public ProductCategory getProductCategory()
@@ -228,10 +228,10 @@ public class BusinessSubscription
             return price.multiply(DAYS_IN_MONTH).multiply(BigDecimal.valueOf(duration.getLength()));
         }
         else if (duration.getUnit().equals(TimeUnit.MONTHS)) {
-            return price.divide(BigDecimal.valueOf(duration.getLength()), SCALE, BigDecimal.ROUND_HALF_UP);
+            return price.divide(BigDecimal.valueOf(duration.getLength()), Rounder.SCALE, BigDecimal.ROUND_HALF_UP);
         }
         else if (duration.getUnit().equals(TimeUnit.YEARS)) {
-            return price.divide(BigDecimal.valueOf(duration.getLength()), SCALE, RoundingMode.HALF_UP).divide(MONTHS_IN_YEAR, SCALE, RoundingMode.HALF_UP);
+            return price.divide(BigDecimal.valueOf(duration.getLength()), Rounder.SCALE, RoundingMode.HALF_UP).divide(MONTHS_IN_YEAR, Rounder.SCALE, RoundingMode.HALF_UP);
         }
         else {
             log.error("Unknown duration [" + duration + "], can't compute mrr");
@@ -239,16 +239,6 @@ public class BusinessSubscription
         }
     }
 
-    public static double round(final BigDecimal decimal)
-    {
-        if (decimal == null) {
-            return 0;
-        }
-        else {
-            return decimal.setScale(SCALE, BigDecimal.ROUND_HALF_UP).doubleValue();
-        }
-    }
-
     @Override
     public String toString()
     {
@@ -288,13 +278,13 @@ public class BusinessSubscription
         if (currency != null ? !currency.equals(that.currency) : that.currency != null) {
             return false;
         }
-        if (mrr != null ? !(round(mrr) == round(that.mrr)) : that.mrr != null) {
+        if (mrr != null ? !(Rounder.round(mrr) == Rounder.round(that.mrr)) : that.mrr != null) {
             return false;
         }
         if (phase != null ? !phase.equals(that.phase) : that.phase != null) {
             return false;
         }
-        if (price != null ? !(round(price) == round(that.price)) : that.price != null) {
+        if (price != null ? !(Rounder.round(price) == Rounder.round(that.price)) : that.price != null) {
             return false;
         }
         if (productCategory != null ? !productCategory.equals(that.productCategory) : that.productCategory != null) {
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountBinder.java b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountBinder.java
new file mode 100644
index 0000000..57b8848
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountBinder.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.dao;
+
+import com.google.common.base.Joiner;
+import com.ning.billing.analytics.BusinessAccount;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@BindingAnnotation(BusinessAccountBinder.BacBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface BusinessAccountBinder
+{
+    public static class BacBinderFactory implements BinderFactory
+    {
+        private final Joiner joiner = Joiner.on(";").skipNulls();
+
+        public Binder build(final Annotation annotation)
+        {
+            return new Binder<BusinessAccountBinder, BusinessAccount>()
+            {
+                public void bind(final SQLStatement q, final BusinessAccountBinder bind, final BusinessAccount account)
+                {
+                    final DateTime dateTimeNow = new DateTime(DateTimeZone.UTC);
+
+                    if (account.getCreatedDt() != null) {
+                        q.bind("created_dt", account.getCreatedDt().getMillis());
+                    }
+                    else {
+                        q.bind("created_dt", dateTimeNow.getMillis());
+                    }
+                    q.bind("updated_dt", dateTimeNow.getMillis());
+
+                    q.bind("account_key", account.getKey());
+                    q.bind("balance", account.getRoundedBalance());
+                    q.bind("tags", joiner.join(account.getTags()));
+                    q.bind("last_invoice_date", account.getLastInvoiceDate().getMillis());
+                    q.bind("total_invoice_balance", account.getRoundedTotalInvoiceBalance());
+                    q.bind("payment_method", account.getPaymentMethod());
+                    q.bind("credit_card_type", account.getCreditCardType());
+                    q.bind("billing_address_country", account.getBillingAddressCountry());
+                }
+            };
+        }
+    }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountDao.java
new file mode 100644
index 0000000..7223f82
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountDao.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.dao;
+
+import com.ning.billing.analytics.BusinessAccount;
+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.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+
+@ExternalizedSqlViaStringTemplate3()
+@RegisterMapper(BusinessAccountMapper.class)
+public interface BusinessAccountDao
+{
+    @SqlQuery
+    BusinessAccount getAccount(@Bind("account_key") final String key);
+
+    @SqlUpdate
+    int createAccount(@BusinessAccountBinder final BusinessAccount account);
+
+    @SqlUpdate
+    int saveAccount(@BusinessAccountBinder final BusinessAccount account);
+
+    @SqlUpdate
+    void test();
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountMapper.java b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountMapper.java
new file mode 100644
index 0000000..46ac794
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountMapper.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.dao;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import com.ning.billing.analytics.BusinessAccount;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class BusinessAccountMapper implements ResultSetMapper<BusinessAccount>
+{
+    private final Splitter splitter = Splitter.on(";").trimResults().omitEmptyStrings();
+
+    @Override
+    public BusinessAccount map(final int index, final ResultSet r, final StatementContext ctx) throws SQLException
+    {
+        final List<String> tags = new ArrayList<String>();
+        Iterables.addAll(tags, splitter.split(r.getString(5)));
+
+        final BusinessAccount account = new BusinessAccount(
+            r.getString(1),
+            BigDecimal.valueOf(r.getDouble(4)),
+            tags,
+            new DateTime(r.getLong(6), DateTimeZone.UTC),
+            BigDecimal.valueOf(r.getDouble(7)),
+            r.getString(8),
+            r.getString(9),
+            r.getString(10)
+        );
+        account.setCreatedDt(new DateTime(r.getLong(2), DateTimeZone.UTC));
+        account.setUpdatedDt(new DateTime(r.getLong(3), DateTimeZone.UTC));
+
+        return account;
+    }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionDaoProvider.java b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionDaoProvider.java
new file mode 100644
index 0000000..86b5665
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionDaoProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.dao;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import org.skife.jdbi.v2.DBI;
+
+public class BusinessSubscriptionTransitionDaoProvider implements Provider<BusinessSubscriptionTransitionDao>
+{
+    private final DBI dbi;
+
+    @Inject
+    public BusinessSubscriptionTransitionDaoProvider(final DBI dbi)
+    {
+        this.dbi = dbi;
+    }
+
+    @Override
+    public BusinessSubscriptionTransitionDao get()
+    {
+        return dbi.onDemand(BusinessSubscriptionTransitionDao.class);
+    }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java b/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java
index 46e9131..7c74808 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java
@@ -18,14 +18,17 @@ package com.ning.billing.analytics.setup;
 
 
 import com.google.inject.AbstractModule;
-import com.ning.billing.analytics.dao.EventDao;
-import com.ning.billing.analytics.dao.EventDaoProvider;
+import com.ning.billing.analytics.dao.BusinessAccountDao;
+import com.ning.billing.analytics.dao.BusinessAccountDaoProvider;
+import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
+import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDaoProvider;
 
 public class AnalyticsModule extends AbstractModule
 {
     @Override
     protected void configure()
     {
-        bind(EventDao.class).toProvider(EventDaoProvider.class).asEagerSingleton();
+        bind(BusinessSubscriptionTransitionDao.class).toProvider(BusinessSubscriptionTransitionDaoProvider.class).asEagerSingleton();
+        bind(BusinessAccountDao.class).toProvider(BusinessAccountDaoProvider.class).asEagerSingleton();
     }
 }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/utils/Rounder.java b/analytics/src/main/java/com/ning/billing/analytics/utils/Rounder.java
new file mode 100644
index 0000000..0f0adef
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/utils/Rounder.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.utils;
+
+import java.math.BigDecimal;
+
+public class Rounder
+{
+    public static final int SCALE = 4;
+
+    public static double round(final BigDecimal decimal)
+    {
+        if (decimal == null) {
+            return 0;
+        }
+        else {
+            return decimal.setScale(SCALE, BigDecimal.ROUND_HALF_UP).doubleValue();
+        }
+    }
+}
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountDao.sql.stg
new file mode 100644
index 0000000..b432fd6
--- /dev/null
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountDao.sql.stg
@@ -0,0 +1,63 @@
+group BusinessAccount;
+
+getAccount(account_key) ::= <<
+  select
+    account_key
+  , created_dt
+  , updated_dt
+  , balance
+  , tags
+  , last_invoice_date
+  , total_invoice_balance
+  , payment_method
+  , credit_card_type
+  , billing_address_country
+  from bac
+  where account_key=:account_key
+  limit 1
+  ;
+>>
+
+createAccount() ::= <<
+  insert into bac(
+    account_key
+  , created_dt
+  , updated_dt
+  , balance
+  , tags
+  , last_invoice_date
+  , total_invoice_balance
+  , payment_method
+  , credit_card_type
+  , billing_address_country
+  ) values (
+    :account_key
+  , :created_dt
+  , :updated_dt
+  , :balance
+  , :tags
+  , :last_invoice_date
+  , :total_invoice_balance
+  , :payment_method
+  , :credit_card_type
+  , :billing_address_country
+  );
+>>
+
+saveAccount() ::= <<
+  update bac set
+    updated_dt=:updated_dt
+  , balance=:balance
+  , tags=:tags
+  , last_invoice_date=:last_invoice_date
+  , total_invoice_balance=:total_invoice_balance
+  , payment_method=:payment_method
+  , credit_card_type=:credit_card_type
+  , billing_address_country=:billing_address_country
+  where account_key=:account_key
+  ;
+>>
+
+test() ::= <<
+  select 1 from bac;
+>>
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 f7be5f4..037c141 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/ddl.sql
+++ b/analytics/src/main/resources/com/ning/billing/analytics/ddl.sql
@@ -28,5 +28,19 @@ create table bst (
 , next_subscription_id varchar(100) default null
 , next_bundle_id varchar(100) default null
 ) engine=innodb;
-
 create index bst_key_index on bst (event_key, requested_timestamp asc);
+
+drop table if exists bac;
+create table bac (
+  account_key varchar(50) not null
+, created_dt bigint not null
+, updated_dt bigint not null
+, balance numeric(10, 4) default 0
+, tags varchar(500) default null
+, last_invoice_date bigint default null
+, total_invoice_balance numeric(10, 4) default 0
+, payment_method varchar(100) default null
+, credit_card_type varchar(32) default null
+, billing_address_country varchar(100) default null
+) engine=innodb;
+create unique index bac_key_index on bac (account_key);
\ No newline at end of file
diff --git a/analytics/src/test/java/com/ning/billing/analytics/TestBusinessAccount.java b/analytics/src/test/java/com/ning/billing/analytics/TestBusinessAccount.java
new file mode 100644
index 0000000..8acc4d2
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/TestBusinessAccount.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics;
+
+import org.joda.time.DateTime;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.math.BigDecimal;
+import java.util.Collections;
+
+public class TestBusinessAccount
+{
+    private BusinessAccount account;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() throws Exception
+    {
+        account = new BusinessAccount("pierre", BigDecimal.ONE, Collections.singletonList("batch15"), new DateTime(), BigDecimal.TEN, "CreditCard", "Visa", "");
+    }
+
+    @Test(groups = "fast")
+    public void testEquals() throws Exception
+    {
+        Assert.assertSame(account, account);
+        Assert.assertEquals(account, account);
+        Assert.assertTrue(account.equals(account));
+
+        final BusinessAccount otherAccount = new BusinessAccount("pierre", BigDecimal.ONE, Collections.singletonList("batch15"), new DateTime(), BigDecimal.TEN, "CreditCard", "Visa", "");
+        Assert.assertFalse(account.equals(otherAccount));
+    }
+}